diff --git a/.circleci/config.yml b/.circleci/config.yml index 2354bac9ff0..b923d9ec4d7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -123,7 +123,7 @@ jobs: environment: discovery.type: single-node resource_class: xlarge - parallelism: 4 + parallelism: 5 steps: - attach_workspace: at: ~/ @@ -135,7 +135,7 @@ jobs: command: | SKIP_CACHE_INVALIDATION=true AWS_ACCESS_KEY_ID=noop AWS_SECRET_ACCESS_KEY=noop npm run start:api:ci > /tmp/web-client/server-output.txt & ./wait-until.sh http://localhost:3000/api/swagger - TESTFILES=$(circleci tests glob "web-client/integration-tests/*.test.js" | circleci tests split --split-by=timings) + TESTFILES=$(circleci tests glob "web-client/integration-tests/*.test.js" "web-client/integration-tests-public/*.test.js" | circleci tests split --split-by=timings) CI=true NO_SCANNER=true SKIP_VIRUS_SCAN=true AWS_ACCESS_KEY_ID=noop AWS_SECRET_ACCESS_KEY=noop npx jest --coverageDirectory=./coverage-integration-${CIRCLE_NODE_INDEX} --runInBand --config web-client/jest-integration.config.js ${TESTFILES} - store_artifacts: path: /tmp/web-client @@ -161,6 +161,7 @@ jobs: ./web-client/coverage-integration-1/coverage-final.json \ ./web-client/coverage-integration-2/coverage-final.json \ ./web-client/coverage-integration-3/coverage-final.json \ + ./web-client/coverage-integration-4/coverage-final.json \ ./web-client/coverage-unit/coverage-final.json - run: name: Check Coverage @@ -254,6 +255,45 @@ jobs: - store_artifacts: path: /tmp/cypress + e2e-cypress-public: + docker: + - image: $AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/ef-cms-us-east-1:latest + aws_auth: + aws_access_key_id: $AWS_ACCESS_KEY_ID + aws_secret_access_key: $AWS_SECRET_ACCESS_KEY + - image: amazon/dynamodb-local + command: ['-jar', 'DynamoDBLocal.jar', '-sharedDb'] + - image: elastic/elasticsearch:7.5.2 + environment: + discovery.type: single-node + environment: + _JAVA_OPTIONS: '-Xms1024m -Xmx2048m' + resource_class: xlarge + steps: + - attach_workspace: + at: ~/ + - run: + name: Create Cypress Artifacts Directory + command: mkdir /tmp/cypress + - run: + name: Cypress + command: | + npm run build:all && + CYPRESS_VERSION=`./node_modules/.bin/cypress --version | awk -F' ' '{print $4; exit}'` + if [ ! -e "/root/.cache/Cypress/${CYPRESS_VERSION}/Cypress/Cypress" ]; then + ./node_modules/.bin/cypress install + fi + + SKIP_CACHE_INVALIDATION=true TEMP_DOCUMENTS_BUCKET_NAME=noop-temp-documents-local-us-east-1 DOCUMENTS_BUCKET_NAME=noop-documents-local-us-east-1 S3_ENDPOINT=http://localhost:9000 MASTER_DYNAMODB_ENDPOINT=http://localhost:8000 AWS_ACCESS_KEY_ID=S3RVER AWS_SECRET_ACCESS_KEY=S3RVER SLS_DEPLOYMENT_BUCKET=noop npx run-p start:api:ci start:public:ci > /tmp/cypress/cypress-output.txt & + ./wait-until-services.sh + ./wait-until.sh http://localhost:3000/api/swagger + sleep 5 + npm run cypress:public + - store_artifacts: + path: /root/project/cypress/videos/ + - store_artifacts: + path: /tmp/cypress + pre-deploy: machine: docker_layer_caching: true @@ -358,6 +398,9 @@ jobs: - run: name: 'Deploy - Web API - Serverless - Practitioners - us-east-1' command: docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" -e "IRS_SUPERUSER_EMAIL=${IRS_SUPERUSER_EMAIL}" -v $(pwd)/.cache:/home/app/.cache --rm efcms /bin/sh -c "./web-api/run-serverless-practitioners.sh ${ENV} us-east-1" + - run: + name: 'Deploy - Web API - Serverless - Messages - us-east-1' + command: docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" -e "IRS_SUPERUSER_EMAIL=${IRS_SUPERUSER_EMAIL}" -v $(pwd)/.cache:/home/app/.cache --rm efcms /bin/sh -c "./web-api/run-serverless-messages.sh ${ENV} us-east-1" - run: name: 'Deploy - Web API - Serverless - Notifications - us-east-1' command: docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" -e "IRS_SUPERUSER_EMAIL=${IRS_SUPERUSER_EMAIL}" -v $(pwd)/.cache:/home/app/.cache --rm efcms /bin/sh -c "./web-api/run-serverless-notifications.sh ${ENV} us-east-1" @@ -436,6 +479,9 @@ jobs: - run: name: 'Deploy - Web API - Serverless - Practitioners - us-west-1' command: docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" -e "IRS_SUPERUSER_EMAIL=${IRS_SUPERUSER_EMAIL}" -v $(pwd)/.cache:/home/app/.cache --rm efcms /bin/sh -c "./web-api/run-serverless-practitioners.sh ${ENV} us-west-1" + - run: + name: 'Deploy - Web API - Serverless - Messages - us-west-1' + command: docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" -e "IRS_SUPERUSER_EMAIL=${IRS_SUPERUSER_EMAIL}" -v $(pwd)/.cache:/home/app/.cache --rm efcms /bin/sh -c "./web-api/run-serverless-messages.sh ${ENV} us-west-1" - run: name: 'Deploy - Web API - Serverless - Notifications - us-west-1' command: docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" -e "IRS_SUPERUSER_EMAIL=${IRS_SUPERUSER_EMAIL}" -v $(pwd)/.cache:/home/app/.cache --rm efcms /bin/sh -c "./web-api/run-serverless-notifications.sh ${ENV} us-west-1" @@ -462,10 +508,10 @@ jobs: # command: docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" --rm efcms /bin/sh -c "cd web-api && ./switch-environment-color.sh ${ENV}" - run: name: 'Deploy - Web Client - S3' - command: docker run -e "DYNAMSOFT_URL_OVERRIDE=${DYNAMSOFT_URL_OVERRIDE}" -e "ENV=${ENV}" -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "COGNITO_SUFFIX=${COGNITO_SUFFIX}" -e "CIRCLE_SHA1=${CIRCLE_SHA1}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" --rm efcms /bin/sh -c "./web-client/build-dist.sh $ENV && aws s3 sync dist s3://ui-${ENV}.${EFCMS_DOMAIN} --delete --cache-control no-cache && aws s3 sync dist s3://failover-ui-${ENV}.${EFCMS_DOMAIN} --delete --cache-control no-cache" + command: docker run -e "DYNAMSOFT_URL_OVERRIDE=${DYNAMSOFT_URL_OVERRIDE}" -e "ENV=${ENV}" -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "COGNITO_SUFFIX=${COGNITO_SUFFIX}" -e "CIRCLE_SHA1=${CIRCLE_SHA1}" -e "CIRCLE_HONEYBADGER_API_KEY=${CIRCLE_HONEYBADGER_API_KEY}" --rm efcms /bin/sh -c "./web-client/build-dist.sh $ENV && aws s3 sync dist s3://ui-${ENV}.${EFCMS_DOMAIN} --delete && aws s3 cp s3://ui-${ENV}.${EFCMS_DOMAIN}/index.html s3://ui-${ENV}.${EFCMS_DOMAIN}/index.html --metadata-directive REPLACE --cache-control max-age=0 && aws s3 sync dist s3://failover-ui-${ENV}.${EFCMS_DOMAIN} --delete --cache-control no-cache && aws s3 cp s3://failover-ui-${ENV}.${EFCMS_DOMAIN}/index.html s3://failover-ui-${ENV}.${EFCMS_DOMAIN}/index.html --metadata-directive REPLACE --cache-control max-age=0" - run: name: 'Deploy - Public Web Client - S3' - command: docker run -e "ENV=${ENV}" -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "COGNITO_SUFFIX=${COGNITO_SUFFIX}" -e "CIRCLE_SHA1=${CIRCLE_SHA1}" --rm efcms /bin/sh -c "./web-client/build-dist-public.sh $ENV && aws s3 sync dist-public s3://ui-public-${ENV}.${EFCMS_DOMAIN} --delete --cache-control no-cache && aws s3 sync dist-public s3://failover-ui-public-${ENV}.${EFCMS_DOMAIN} --delete --cache-control no-cache" + command: docker run -e "ENV=${ENV}" -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "EFCMS_DOMAIN=${EFCMS_DOMAIN}" -e "COGNITO_SUFFIX=${COGNITO_SUFFIX}" -e "CIRCLE_SHA1=${CIRCLE_SHA1}" --rm efcms /bin/sh -c "./web-client/build-dist-public.sh $ENV && aws s3 sync dist-public s3://ui-public-${ENV}.${EFCMS_DOMAIN} --delete && aws s3 cp s3://ui-public-${ENV}.${EFCMS_DOMAIN}/index.html s3://ui-public-${ENV}.${EFCMS_DOMAIN}/index.html --metadata-directive REPLACE --cache-control max-age=0 && aws s3 sync dist-public s3://failover-ui-public-${ENV}.${EFCMS_DOMAIN} --delete --cache-control no-cache && aws s3 cp s3://failover-ui-public-${ENV}.${EFCMS_DOMAIN}/index.html s3://failover-ui-public-${ENV}.${EFCMS_DOMAIN}/index.html --metadata-directive REPLACE --cache-control max-age=0" post-deploy: machine: @@ -493,22 +539,22 @@ jobs: - run: name: 'Deploy - Web API - Cognito Create Users' command: | - if [ "${CIRCLE_BRANCH}" == "staging" ] || [ "${CIRCLE_BRANCH}" == "irs" ]; then + if [ "${CIRCLE_BRANCH}" == "develop" ] || [ "${CIRCLE_BRANCH}" == "staging" ] || [ "${CIRCLE_BRANCH}" == "irs" ] || [ "${CIRCLE_BRANCH}" == "experimental1" ] || [ "${CIRCLE_BRANCH}" == "experimental2" ] ; then docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "USTC_ADMIN_PASS=${USTC_ADMIN_PASS}" --rm efcms /bin/sh -c "cd web-api && ./setup-cognito-users.sh ${ENV}" else echo "skipping…" fi - run: name: 'Deploy - Web API - Cognito Create Court Users' - command: | - if [ "${CIRCLE_BRANCH}" == "staging" ] || [ "${CIRCLE_BRANCH}" == "test" ]; then + command: | + if [ "${CIRCLE_BRANCH}" == "develop" ] || [ "${CIRCLE_BRANCH}" == "staging" ] || [ "${CIRCLE_BRANCH}" == "test" ] || [ "${CIRCLE_BRANCH}" == "experimental1" ] || [ "${CIRCLE_BRANCH}" == "experimental2" ]; then docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "USTC_ADMIN_PASS=${USTC_ADMIN_PASS}" --rm efcms /bin/sh -c "cd web-api && ./setup-court-users.sh ${ENV}" else echo "skipping…" fi - run: name: 'Deploy - Web API - Cognito Create IRS User' - command: | + command: | if [ "${CIRCLE_BRANCH}" == "irs" ]; then docker run -e "AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -e "AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -e "USTC_ADMIN_PASS=${USTC_ADMIN_PASS}" --rm efcms /bin/sh -c "cd web-api && ./setup-irs-user.sh ${ENV}" else @@ -578,6 +624,9 @@ workflows: - e2e-cypress: requires: - bundle + - e2e-cypress-public: + requires: + - bundle - build-client-coverage: requires: - build-shared @@ -586,6 +635,7 @@ workflows: - build-client-integration - e2e-pa11y - e2e-cypress + - e2e-cypress-public - pre-deploy: requires: - build-client-coverage @@ -658,6 +708,13 @@ workflows: branches: ignore: - develop + - e2e-cypress-public: + requires: + - bundle + filters: + branches: + ignore: + - develop - build-client-coverage: requires: - build-shared @@ -666,6 +723,7 @@ workflows: - build-client-integration - e2e-pa11y - e2e-cypress + - e2e-cypress-public filters: branches: ignore: @@ -681,7 +739,7 @@ workflows: - test - migration - master - - experimental + - experimental1 - experimental2 - deploy-api-east: requires: @@ -694,7 +752,7 @@ workflows: - test - migration - master - - experimental + - experimental1 - experimental2 - deploy-api-west: requires: @@ -707,7 +765,7 @@ workflows: - test - migration - master - - experimental + - experimental1 - experimental2 - switch-and-deploy-ui: requires: @@ -721,7 +779,7 @@ workflows: - test - migration - master - - experimental + - experimental1 - experimental2 - post-deploy: requires: @@ -734,5 +792,5 @@ workflows: - test - migration - master - - experimental + - experimental1 - experimental2 diff --git a/.eslintrc.js b/.eslintrc.js index df4ae40c5d3..feed186b1b6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -33,6 +33,15 @@ module.exports = { 'promise/catch-or-return': 'off', }, }, + { + files: [ + 'web-client/integration-tests/**/*.js', + 'web-client/integration-tests-public/**/*.js', + ], + rules: { + 'jest/expect-expect': 'off', + }, + }, ], parser: 'babel-eslint', parserOptions: { @@ -218,6 +227,7 @@ module.exports = { 'dynamsoft', 'efcms', 'elasticsearch', + 'enum', 'eslint', 'falsy', 'fieldset', @@ -324,6 +334,7 @@ module.exports = { 'textarea', 'thorton', 'thortons', + 'todays', 'touchmove', 'transferee', 'truthy', diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 3ceca225a64..0c97b8743bf 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -2,9 +2,9 @@ name: Node.js CI on: push: - branches: [ develop, experimental, master ] + branches: [ develop, experimental1, master ] pull_request: - branches: [ develop, experimental, master ] + branches: [ develop, experimental1, master ] jobs: linting: @@ -26,4 +26,3 @@ jobs: run: npm run lint - name: Shellcheck run: ./run-shellcheck.sh - diff --git a/.gitignore b/.gitignore index 8b4d2ce9a2b..194848c8dce 100644 --- a/.gitignore +++ b/.gitignore @@ -23,15 +23,17 @@ graph-generators/node_modules/ lambda-policy.json node_modules/ parcel-debug-* +proxy-request-times.json puppeteer_lambda_layer.tar.gz +reports/test-reporter.xml shared/coverage shared/pdf-tests/*.html web-api/runtimes/clamav/bin web-api/runtimes/clamav/clamav_lambda_layer.tar.gz web-api/runtimes/clamav/lib web-api/storage/s3/* -web-api/terraform/template/cognito-triggers/index.js.zip web-api/terraform/template/cognito-authorizer/index.js.zip +web-api/terraform/template/cognito-triggers/index.js.zip web-api/terraform/template/log-forwarder/index.js.zip web-client/.cache/ web-client/coverage-e2e @@ -39,7 +41,5 @@ web-client/coverage-integration/* web-client/coverage-unit/* web-client/pa11y/pa11y-screenshots web-client/reports -web-client/tests_output/ web-client/terraform/common/cloudfront-edge/index.js.zip -proxy-request-times.json -reports/test-reporter.xml +web-client/tests_output/ diff --git a/.prettierignore b/.prettierignore index 9507e4cdb53..dd9f420d19b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,3 @@ package.json *.md -*_.js \ No newline at end of file +*_.js diff --git a/Dockerfile b/Dockerfile index 323d71bcc87..45a75989b3c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,4 @@ -FROM cypress/base:12.16.0 - -RUN echo "recache again" +FROM cypress/base:12.16.2 WORKDIR /home/app @@ -46,6 +44,7 @@ ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 COPY package.json /home/app/package.json COPY package-lock.json /home/app/package-lock.json RUN npm set progress=false && \ + npm config set puppeteer_skip_chromium_download true && \ npm i COPY . /home/app diff --git a/README.md b/README.md index 77417646dcc..f04101fd461 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,6 @@ For documentation about the CI/CD setup, API, style guide, UX, code review, etc. -## Dependency diagrams - -- Client -- Server - ## Backlog The backlog is stored [in GitHub Issues in Flexion’s repository](https://github.com/flexion/ef-cms/issues), _not_ on this repository. Although they can be viewed like any other GitHub issues, they are managed on a scrum board that requires the [ZenHub browser plugin](https://www.zenhub.com/) to see. diff --git a/config/dev.yml b/config/dev.yml index 5a410629412..ef4833446e0 100644 --- a/config/dev.yml +++ b/config/dev.yml @@ -1 +1 @@ -nodeEnv: production \ No newline at end of file +nodeEnv: production diff --git a/config/exp.yml b/config/exp1.yml similarity index 100% rename from config/exp.yml rename to config/exp1.yml diff --git a/config/prod.yml b/config/prod.yml index 5a410629412..ef4833446e0 100644 --- a/config/prod.yml +++ b/config/prod.yml @@ -1 +1 @@ -nodeEnv: production \ No newline at end of file +nodeEnv: production diff --git a/config/stg.yml b/config/stg.yml index 5a410629412..ef4833446e0 100644 --- a/config/stg.yml +++ b/config/stg.yml @@ -1 +1 @@ -nodeEnv: production \ No newline at end of file +nodeEnv: production diff --git a/cypress-public.json b/cypress-public.json new file mode 100644 index 00000000000..2135ee0f410 --- /dev/null +++ b/cypress-public.json @@ -0,0 +1,11 @@ +{ + "baseUrl": "http://localhost:5678", + "reporter": "spec", + "reporterOptions": { + "toConsole": true + }, + "defaultCommandTimeout": 20000, + "requestTimeout": 12000, + "viewportWidth": 1200, + "viewportHeight": 900 +} diff --git a/cypress/integration/assign-a-work-item-to-self.spec.js b/cypress/integration/assign-a-work-item-to-self.spec.js index c62ade3d0c5..669a10e75cd 100644 --- a/cypress/integration/assign-a-work-item-to-self.spec.js +++ b/cypress/integration/assign-a-work-item-to-self.spec.js @@ -1,19 +1,12 @@ const { getWorkItemCheckboxLabel, - getWorkItemMessage, - getWorkItemMessages, getWorkItemRow, navigateTo: navigateToDashboard, selectAssignee, - viewDocumentQCMyInbox, viewDocumentQCSectionInbox, } = require('../support/pages/dashboard'); describe('Assign a work item ', () => { - before(() => { - cy.task('seed'); - }); - it('views the section inbox', () => { navigateToDashboard('petitionsclerk'); viewDocumentQCSectionInbox(); @@ -29,18 +22,5 @@ describe('Assign a work item ', () => { getWorkItemRow('101-19W') .contains('td.to', 'Test Petitionsclerk') .should('exist'); - - getWorkItemRow('101-19W').contains('a', 'Petition').click(); - getWorkItemMessages(); - - getWorkItemMessage('2611344f-f7bf-4f47-8ba0-60c70cb25446').contains( - 'Petition filed by Brett Osborne is ready for review.', - ); - }); - - it('places the work item in the petitionsclerk my inbox', () => { - navigateToDashboard('petitionsclerk'); - viewDocumentQCMyInbox(); - getWorkItemRow('101-19W').should('exist'); }); }); diff --git a/cypress/integration/create-a-work-item.spec.js b/cypress/integration/create-a-work-item.spec.js index 08d9006a80a..26908e4e702 100644 --- a/cypress/integration/create-a-work-item.spec.js +++ b/cypress/integration/create-a-work-item.spec.js @@ -22,7 +22,6 @@ const { describe('Create a work item ', () => { before(() => { - cy.task('seed'); navigateToDocumentDetail( 'petitionsclerk', '104-19', diff --git a/cypress/integration/edit-case-caption-via-case-detail-header.spec.js b/cypress/integration/edit-case-caption-via-case-detail-header.spec.js index 61a5973159a..fd364b3962e 100644 --- a/cypress/integration/edit-case-caption-via-case-detail-header.spec.js +++ b/cypress/integration/edit-case-caption-via-case-detail-header.spec.js @@ -9,7 +9,6 @@ const { describe('Edit a case caption from case detail header', function () { before(() => { - cy.task('seed'); navigateToCaseDetail('docketclerk', '101-19'); getActionMenuButton().click(); getEditCaseCaptionButton().click(); diff --git a/cypress/integration/edit-case-caption-via-petition-qc.spec.js b/cypress/integration/edit-case-caption-via-petition-qc.spec.js index 404c4fca60e..32b7a511c70 100644 --- a/cypress/integration/edit-case-caption-via-petition-qc.spec.js +++ b/cypress/integration/edit-case-caption-via-petition-qc.spec.js @@ -10,7 +10,6 @@ const { describe('change the case caption via the petition qc page', () => { before(() => { - cy.task('seed'); navigateToPetitionQc( 'petitionsclerk', '101-19', diff --git a/cypress/integration/file-a-petition.spec.js b/cypress/integration/file-a-petition.spec.js index 1bae8f66c16..9fdb34a961f 100644 --- a/cypress/integration/file-a-petition.spec.js +++ b/cypress/integration/file-a-petition.spec.js @@ -3,7 +3,6 @@ let createdDocketNumber; describe('File a petition', function () { before(() => { - cy.task('seed'); cy.login('petitioner'); }); @@ -165,12 +164,22 @@ describe('creation form', () => { cy.get('button#submit-case').click(); }); + it('click the regular radio button', () => { + cy.get('#procedure-type-radios').scrollIntoView(); + cy.get('#procedure-type-radios label').eq(1).click(); + }); + + it('select a city', () => { + cy.get('#preferred-trial-city').scrollIntoView().select('Mobile, Alabama'); + }); + it('click the small radio button', () => { cy.get('#procedure-type-radios').scrollIntoView(); - cy.get('#procedure-type-radios label').first().click(); + cy.get('#procedure-type-radios label').eq(0).click(); }); it('select a city', () => { + cy.get('#preferred-trial-city').should('have.value', ''); cy.get('#preferred-trial-city').scrollIntoView().select('Mobile, Alabama'); }); @@ -179,15 +188,20 @@ describe('creation form', () => { }); it('submits forms and redirects to the file petition success page', () => { + cy.get('button#submit-case').scrollIntoView().click(); + cy.server(); cy.route('POST', '**/cases').as('postCase'); - cy.get('button#submit-case').scrollIntoView().click(); cy.wait('@postCase'); cy.get('@postCase').should(xhr => { // eslint-disable-next-line jest/valid-expect expect(xhr.responseBody).to.have.property('docketNumber'); createdDocketNumber = xhr.responseBody.docketNumber; }); + + // wait for elasticsearch to refresh + cy.wait(1000); + cy.url().should('include', 'file-a-petition/success'); cy.get('a#button-back-to-dashboard').click(); }); @@ -209,4 +223,8 @@ describe('can view case detail', () => { it('shows docket record table and data', () => { cy.get('table.case-detail.docket-record tbody tr').should('exist'); }); + + it('displays page count of the petition document', () => { + cy.get('td.number-of-pages').should('contain.text', '2'); + }); }); diff --git a/cypress/integration/file-an-answer.spec.js b/cypress/integration/file-an-answer.spec.js index 6e5680eebc0..d1e88b75a6a 100644 --- a/cypress/integration/file-an-answer.spec.js +++ b/cypress/integration/file-an-answer.spec.js @@ -4,7 +4,6 @@ const { describe('Filing an Answer', function () { before(() => { - cy.task('seed'); cy.login('irsPractitioner', '/case-detail/102-19'); }); @@ -43,6 +42,9 @@ describe('Filing an Answer', function () { }); it('reflects changes to 102-19 by showing it in irsPractitioner case list', () => { + // wait for elasticsearch to refresh + cy.wait(1000); + navigateToDashboard('irsPractitioner'); cy.get('table#case-list').find('a').should('contain', '102-19'); }); diff --git a/cypress/integration/petitions-clerk-creates-a-case.spec.js b/cypress/integration/petitions-clerk-creates-a-case.spec.js index 7e2e8f43b39..f4fb8bb1bb1 100644 --- a/cypress/integration/petitions-clerk-creates-a-case.spec.js +++ b/cypress/integration/petitions-clerk-creates-a-case.spec.js @@ -9,8 +9,6 @@ const { describe('Create case and submit to IRS', function () { before(() => { - cy.task('seed'); - navigateToDocumentQC('petitionsclerk'); getCreateACaseButton().click(); @@ -29,4 +27,9 @@ describe('Create case and submit to IRS', function () { expect(xhr.responseBody).to.have.property('docketNumber'); }); }); + + it('should display a confirmation modal when the user clicks cancel on the review page', () => { + cy.get('button#cancel-create-case').scrollIntoView().click(); + cy.get('div.modal-header').should('exist'); + }); }); diff --git a/cypress/integration/public/advanced-search.spec.js b/cypress/integration/public/advanced-search.spec.js new file mode 100644 index 00000000000..13729024487 --- /dev/null +++ b/cypress/integration/public/advanced-search.spec.js @@ -0,0 +1,49 @@ +const { + clickOnSearchTab, + docketRecordTable, + enterDocumentDocketNumber, + enterDocumentKeywordForOpinionSearch, + enterPetitionerName, + navigateTo: navigateToDashboard, + noSearchResultsContainer, + searchForCaseByDocketNumber, + searchForCaseByPetitionerInformation, + searchForDocuments, + searchResultsTable, +} = require('../../support/pages/public/advanced-search'); + +describe('Advanced search', () => { + describe('case - by name', () => { + it('should route to case detail when a match is found and the user clicks on the docket record link in the table', () => { + navigateToDashboard(); + enterPetitionerName('Osborne'); + searchForCaseByPetitionerInformation(); + expect(searchResultsTable()).to.exist; + }); + }); + + describe('case - by docket number', () => { + it('should display "No Matches Found" when case search yields no results', () => { + navigateToDashboard(); + searchForCaseByDocketNumber('999-99'); + expect(noSearchResultsContainer()).to.exist; + }); + + it('should route to case detail when a case search match is found', () => { + navigateToDashboard(); + searchForCaseByDocketNumber('103-20'); + expect(docketRecordTable()).to.exist; + }); + }); + + describe('opinion', () => { + it('should display results when a keyword and docketNumberWithSuffix is provided', () => { + navigateToDashboard(); + clickOnSearchTab('opinion'); + enterDocumentKeywordForOpinionSearch('opinion'); + enterDocumentDocketNumber('105-20L'); + searchForDocuments(); + expect(searchResultsTable()).to.exist; + }); + }); +}); diff --git a/cypress/integration/start-a-case-practitioner.spec.js b/cypress/integration/start-a-case-practitioner.spec.js index 05118b94d93..418ee965b87 100644 --- a/cypress/integration/start-a-case-practitioner.spec.js +++ b/cypress/integration/start-a-case-practitioner.spec.js @@ -7,10 +7,6 @@ const { const { fillInAndSubmitForm } = require('../support/pages/start-a-case'); describe('Start a case as a practitioner ', () => { - before(() => { - cy.task('seed'); - }); - it('go to the practitioner dashboard and expect that a case list table is displayed with 3 cases', () => { navigateToDashboard('privatePractitioner'); getCaseList().should('have.length', 3); diff --git a/cypress/support/pages/dashboard.js b/cypress/support/pages/dashboard.js index 137a4aa5b18..ff330147760 100644 --- a/cypress/support/pages/dashboard.js +++ b/cypress/support/pages/dashboard.js @@ -19,12 +19,6 @@ exports.viewSectionOutbox = () => { cy.get('button#section-sent-tab').click(); }; -exports.viewDocumentQCMyInbox = () => { - cy.visit('/document-qc/my/inbox'); - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(1000); -}; - exports.viewDocumentQCSectionInbox = () => { cy.visit('/document-qc/section/inbox'); }; @@ -60,11 +54,3 @@ exports.getWorkItemRow = docketNumber => { exports.getSendButton = () => { return cy.contains('button', 'Send'); }; - -exports.getWorkItemMessages = () => { - return cy.get('#tab-pending-messages').click(); -}; - -exports.getWorkItemMessage = workItemId => { - return cy.get(`div.workitem-${workItemId}`); -}; diff --git a/cypress/support/pages/public/advanced-search.js b/cypress/support/pages/public/advanced-search.js new file mode 100644 index 00000000000..2044ee71e2f --- /dev/null +++ b/cypress/support/pages/public/advanced-search.js @@ -0,0 +1,44 @@ +exports.navigateTo = () => { + cy.visit('/'); +}; + +exports.clickOnSearchTab = tabName => { + cy.get(`button#tab-${tabName}`).click(); +}; + +exports.searchForCaseByDocketNumber = docketNumber => { + cy.get('input#docket-number').type(docketNumber); + cy.get('button#docket-search-button').click(); +}; + +exports.enterPetitionerName = name => { + cy.get('input#petitioner-name').type(name); +}; + +exports.enterDocumentKeywordForOpinionSearch = keyword => { + cy.get('input#opinion-search').type(keyword); +}; + +exports.enterDocumentDocketNumber = docketNumber => { + cy.get('input#docket-number').type(docketNumber); +}; + +exports.searchForCaseByPetitionerInformation = () => { + cy.get('button#advanced-search-button').click(); +}; + +exports.searchForDocuments = () => { + cy.get('button#advanced-search-button').click(); +}; + +exports.noSearchResultsContainer = () => { + return cy.get('div#no-search-results'); +}; + +exports.searchResultsTable = () => { + return cy.get('table.search-results'); +}; + +exports.docketRecordTable = () => { + return cy.get('table.docket-record'); +}; diff --git a/cypress/support/pages/start-a-case.js b/cypress/support/pages/start-a-case.js index f5ac8068bc0..1e75e5aa023 100644 --- a/cypress/support/pages/start-a-case.js +++ b/cypress/support/pages/start-a-case.js @@ -37,6 +37,9 @@ exports.fillInAndSubmitForm = () => { // step 5 cy.get('button#submit-case').scrollIntoView().click(); + // wait for elasticsearch to refresh + cy.wait(3000); + // success page cy.url().should('include', 'file-a-petition/success'); cy.get('a#button-back-to-dashboard').click(); diff --git a/docs/CHECKLISTS.md b/docs/CHECKLISTS.md index afa552d5d4f..d915db3d2a2 100644 --- a/docs/CHECKLISTS.md +++ b/docs/CHECKLISTS.md @@ -18,9 +18,8 @@ All of our endpoints are split into multiple AWS CloudFormation stacks and hoste - [ ] create a new `*Handlers.js` file, your functions for your new `serverless-*.yml` must point to this handlers - [ ] create a new `serverless-*.yml` file in the `./web-api` directory (copy an existing one and modify as needed) - [ ] change the service name to something unique - - [ ] change the `customDomain.basePath` to be something unique - - [ ] change `serverless-offline.port` to be a new unique port - [ ] update the `package.include` to use the new handlers file you cretaed + - [ ] add a section to `web-api/config/custom.yml` for the new stack with a unique port and basePath - [ ] update `./web-api/proxy.js` to include your new path and map it to your new port - [ ] create a new `./web-api/run-serverless*.sh` script (chmod 755 or copy an existing script) and modify it to use the new `*Handlers.js` file - [ ] update `./web-api/run-local.sh` to include your new serverless service diff --git a/docs/ENVIRONMENT_DESTRUCTION.md b/docs/ENVIRONMENT_DESTRUCTION.md deleted file mode 100644 index 94b5dcfb9c8..00000000000 --- a/docs/ENVIRONMENT_DESTRUCTION.md +++ /dev/null @@ -1,49 +0,0 @@ -# Environment Destruction - -Follow these instructions to destroy an environment that was previously created and deployed: - -1. In AWS’s [API Gateway console](https://console.aws.amazon.com/apigateway/home?region=us-east-1#/), delete the custom domain for `efcms-[ENV].ustc-case-mgmt.example.gov` and `efcms-[ENV]-ws.ustc-case-mgmt.example.gov` in both us-east-1 and us-west-1 (replacing `example.gov` with your actual domain name). - -2. In AWS’s [CloudFormation console](https://console.aws.amazon.com/cloudformation/), delete all the CloudFormation stacks in both us-east-1 and us-west-1 (both blue and green). The stacks will match the following pattern: `ef-cms-*-[ENV]` (e.g. `env-cms-*-prod`). Simply enter the environment name in the search bar to find all of them. - -3. In AWS’s [DynamoDB console](https://console.aws.amazon.com/dynamodb/), delete the tables for the environment in both us-east-1 and us-west-1. There should be two tables in us-east-1 and one table in us-west-1. - -4. In AWS’s [Cognito console](https://console.aws.amazon.com/cognito/), delete the domain for `efcms-[ENV]`, followed by the Cognito user pool for the environment in us-east-1. - -5. In AWS’s [S3 console](https://console.aws.amazon.com/s3/), empty all of the buckets for the environment. Enter `[ENV]` in the search bar to find all buckets associated with your environment. There should be a total of 10 buckets to empty. - -6. In AWS’s [CloudFront console](https://console.aws.amazon.com/cloudfront/), search for the distributions for the environment by entering `[ENV]` in the search bar. There should be one for ui and one for ui-public. For each one of those, click on it, go to the "Behaviors" tab, and edit the behavior. At the bottom of the Edit page, there is a Lambda Function Associations section. Click the `X` to delete that association. - -7. If not already installed and configured, install the AWS CLI on your local system and configure it to use your IAM credentials. Then destroy web-client: `cd web-client/terraform/main && ../bin/environment-destroy.sh [ENV]`. An expected error will occur: - -``` -* module.environment.aws_lambda_function.header_security_lambda (destroy): 1 error(s) occurred: - -* aws_lambda_function.header_security_lambda: Error deleting Lambda Function: InvalidParameterValueException: Lambda was unable to delete arn:aws:lambda:us-east-1:515554424717:function:header_security_lambda_exp:53 because it is a replicated function. Please see our documentation for Deleting Lambda@Edge Functions and Replicas. -{ - Message_: "Lambda was unable to delete arn:aws:lambda:us-east-1:515554424717:function:header_security_lambda_exp:53 because it is a replicated function. Please see our documentation for Deleting Lambda@Edge Functions and Replicas." -} -``` - -This Lambda function's replicas may take several hours to remove. It can be deleted from the AWS console after waiting a few hours for replication deletion. - -8. Destroy web-api: `cd web-api/terraform/main && ../bin/environment-destroy.sh [ENV]`. - -If you get an error like this, you may have to contact AWS and have them remove the associations with the certificate: - -``` -* aws_acm_certificate.ws-us-west-1: Error deleting certificate: ResourceInUseException: Certificate arn:aws:acm:us-west-1:515554424717:certificate/9d6bbec6-c7fc-4277-87a5-fb63f2589f21 in account 515554424717 is in use. -``` - -If you run into this issue then you'll need to run `terraform state rm aws_cognito_user_pool_domain.main`: - -``` -Error: Error applying plan: - -1 error(s) occurred: - -* module.ef-cms_apis.aws_cognito_user_pool_domain.main (destroy): 1 error(s) occurred: - -* aws_cognito_user_pool_domain.main: InvalidParameter: 1 validation error(s) found. -- minimum field size of 1, DeleteUserPoolDomainInput.UserPoolId. -``` diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 3e787a5fed1..ed47c6915e2 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -116,3 +116,12 @@ To revert your `serverless-prune-plugin`, just change `before` back to `after` o This error often occurs when we are indexing too many dynamic keys or nested objects with Elasticsearch and reach our total field limit. The script will output data related to the mapping for the environment and can help pinpoint areas to look into further. To filter data from indexing, add fields or keys to the filtering functions in `processStreamRecordsInteractor.js`. + + +### NotFoundException when calling the GetDomainName + +This error occurs when code changes do not cause the checksums of the files to change. In order to redeploy, the file must have a different checksum than what is recorded in the database. + +#### Solution + +Delete checksums from the environment dynamo table that is failing. For example, if the dev deploy is failing, navigate to the efcms-deploy-dev table in AWS and delete all the check-sum-** items. \ No newline at end of file diff --git a/docs/entities/Batch.md b/docs/entities/Batch.md new file mode 100644 index 00000000000..6b2b5775e78 --- /dev/null +++ b/docs/entities/Batch.md @@ -0,0 +1,45 @@ +# Batch + ``` +--- + type: "object" + keys: + batchId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + batchIndex: + type: "number" + flags: + presence: "required" + rules: + - + name: "integer" + - + name: "min" + args: + limit: 0 + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + pages: + type: "array" + flags: + presence: "required" + rules: + - + name: "min" + args: + limit: 1 + + ``` diff --git a/docs/entities/Case.md b/docs/entities/Case.md index bcf6720ed68..9d83f7e37ef 100644 --- a/docs/entities/Case.md +++ b/docs/entities/Case.md @@ -1,835 +1,1090 @@ # Case - -### associatedJudge - - -Judge assigned to this case. Defaults to Chief Judge. - - -Restricted - -> `string` | optional - -##### Maximum limit - - -`50` - -### automaticBlocked - - -Temporarily blocked from trial due to a pending item or due date. - -> `boolean` | optional - -### automaticBlockedDate - -> `any` - - -If `automaticBlocked` = `true`, then this field is `date` and is `required.` - - -Otherwise, this field is `any` and is `optional`. `null` is allowed. - -### automaticBlockedReason - -> `any` - - -If `automaticBlocked` = `true`, then this field is `string` and is `required.` - - -Otherwise, this field is `any` and is `optional`. `null` is allowed. - -### blocked - - -Temporarily blocked from trial. - - -Restricted - -> `boolean` | optional - -### blockedDate - - -Restricted - -> `any` - - -If `blocked` = `true`, then this field is `date` and is `required.` - - -Otherwise, this field is `any` and is `optional`. `null` is allowed. - -### blockedReason - - -Restricted - -> `any` - - -If `blocked` = `true`, then this field is `string` and is `required.` - - -Otherwise, this field is `any` and is `optional`. `null` is allowed. - -### caseCaption - - -The name of the party bringing the case, e.g. "Carol Williams, Petitioner," "Mark Taylor, Incompetent, Debra Thomas, Next Friend, Petitioner," or "Estate of Test Taxpayer, Deceased, Petitioner." This is the first half of the case title. - -> `string` | required - -##### Maximum limit - - -`500` - -### caseId - - -Unique case ID only used by the system. - -> `string` | required - -### caseNote - - -Restricted - -> `string` | optional - -##### Maximum limit - - -`500` - -### caseType - -> `string` | required - -##### Allowed Values - - - - `CDP (Lien/Levy)` - - `Deficiency` - - `Declaratory Judgment (Exempt Organization)` - - `Declaratory Judgment (Retirement Plan)` - - `Innocent Spouse` - - `Interest Abatement` - - `Other` - - `Partnership (BBA Section 1101)` - - `Partnership (Section 6226)` - - `Partnership (Section 6228)` - - `Passport` - - `Whistleblower` - - `Worker Classification` - -### contactPrimary - -> `object` | required - -### contactSecondary - -> `object` | optional - -##### Can be null. - -### createdAt - - -When the paper or electronic case was added to the system. This value cannot be edited. - -> `date` | required - -### docketNumber - - -Unique case ID in XXXXX-YY format. - -> `string` | required - -##### Regex Pattern - - -`/^(\d{3,5}-\d{2})$/` - -### docketNumberSuffix - -> `string` | optional - -##### Allowed Values - - - - `null` - - `W` - - `P` - - `X` - - `R` - - `SL` - - `L` - - `S` - -### docketRecord - - -List of DocketRecord Entities for the case. - -> `array` | required - - -An array of [`DocketRecord`](./DocketRecord.md)s - -#### Rules - -### documents - - -List of Document Entities for the case. - -> `array` | required - - -An array of [`Document`](./Document.md)s - -### entityName - -> `string` | required - -##### Can be Case. - -### filingType - -> `string` | optional - -##### Allowed Values - - - - `Myself` - - `Myself and my spouse` - - `A business` - - `Other` - - `Individual petitioner` - - `Petitioner and spouse` - -### hasVerifiedIrsNotice - - -Whether the petitioner received an IRS notice, verified by the petitions clerk. - -> `boolean` | optional - -##### Can be null. - -### highPriority - - -Restricted - -> `boolean` | optional - -### highPriorityReason - - -Restricted - -> `any` - - -If `highPriority` = `true`, then this field is `string` and is `required.` - - -Otherwise, this field is `any` and is `optional`. `null` is allowed. - -### initialCaption - - -Case caption before modification. - -> `string` | optional - -##### Maximum limit - - -`500` - -##### Can be null. - -### initialDocketNumberSuffix - - -Case docket number suffix before modification. - -> `string` | optional - -##### Maximum limit - - -`2` - -##### Can be null. - -### irsNoticeDate - - -Last date that the petitioner is allowed to file before. - -> `date` | optional - -##### Maximum date - - -`now` - -##### Can be null. - -### irsPractitioners - - -List of IRS practitioners (also known as respondents) associated with the case. - -> `array` | optional - -### isPaper - -> `boolean` | optional - -### leadCaseId - - -If this case is consolidated, this is the ID of the lead case. It is the lowest docket number in the consolidated group. - -> `string` | optional - -### mailingDate - - -Date that petition was mailed to the court. - -> `any` - - -If `isPaper` = `true`, then this field is `string` and is `required.` - - -Otherwise, this field is `string` and is `optional`. `null` is allowed. - -### noticeOfAttachments - - -Reminder for clerks to review the notice of attachments. - -> `boolean` | optional - -### noticeOfTrialDate - - -Reminder for clerks to review the notice of trial date. - -> `date` | optional - -### orderDesignatingPlaceOfTrial - - -Reminder for clerks to review the Order Designating Place of Trial. - -> `boolean` | optional - -### orderForAmendedPetition - - -Reminder for clerks to review the order for amended Petition. - -> `boolean` | optional - -### orderForAmendedPetitionAndFilingFee - - -Reminder for clerks to review the order for amended Petition And filing fee. - -> `boolean` | optional - -### orderForFilingFee - - -Reminder for clerks to review the order for filing fee. - -> `boolean` | optional - -### orderForOds - - -Reminder for clerks to review the order for ODS. - -> `boolean` | optional - -### orderForRatification - - -Reminder for clerks to review the Order for Ratification. - -> `boolean` | optional - -### orderToChangeDesignatedPlaceOfTrial - - -Reminder for clerks to review the Order to Change Designated Place Of Trial. - -> `boolean` | optional - -### orderToShowCause - - -Reminder for clerks to review the Order to Show Cause. - -> `boolean` | optional - -### partyType - - -Party type of the case petitioner. - -> `string` | required - -##### Allowed Values - - - - `Conservator` - - `Corporation` - - `Custodian` - - `Donor` - - `Estate with an executor/personal representative/fiduciary/etc.` - - `Estate without an executor/personal representative/fiduciary/etc.` - - `Guardian` - - `Next friend for a legally incompetent person (without a guardian, conservator, or other like fiduciary)` - - `Next friend for a minor (without a guardian, conservator, or other like fiduciary)` - - `Partnership (as the Tax Matters Partner)` - - `Partnership (as a partnership representative under the BBA regime)` - - `Partnership (as a partner other than Tax Matters Partner)` - - `Petitioner` - - `Petitioner & deceased spouse` - - `Petitioner & spouse` - - `Surviving spouse` - - `Transferee` - - `Trust` - -### petitionPaymentStatus - - -Status of the case fee payment. - -> `string` | required - -##### Allowed Values - - - - `Paid` - - `Not Paid` - - `Waived` - -### petitionPaymentDate - - -When the petitioner paid the case fee. - -> `any` - - -If `petitionPaymentStatus` = `Paid`, then this field is `date` and is `required.` - - -Otherwise, this field is `date` and is `optional`. `null` is allowed. - -### petitionPaymentMethod - - -How the petitioner paid the case fee. - -> `any` - - -If `petitionPaymentStatus` = `Paid`, then this field is `string` and is `required.` - - -Otherwise, this field is `string` and is `optional`. `null` is allowed. - -### petitionPaymentWaivedDate - - -When the case fee was waived. - -> `any` - - -If `petitionPaymentStatus` = `Waived`, then this field is `date` and is `required.` - - -Otherwise, this field is `date` and is `optional`. `null` is allowed. - -### preferredTrialCity - - -Where the petitioner would prefer to hold the case trial. - -> `conditional` | optional - - -*Must match 1 of the following conditions:* - -#### Condition #1 for `preferredTrialCity`: - -> `string` - -##### Allowed Values - - - - `Fresno, California` - - `Tallahassee, Florida` - - `Pocatello, Idaho` - - `Peoria, Illinois` - - `Wichita, Kansas` - - `Shreveport, Louisiana` - - `Portland, Maine` - - `Billings, Montana` - - `Albany, New York` - - `Syracuse, New York` - - `Bismarck, North Dakota` - - `Aberdeen, South Dakota` - - `Burlington, Vermont` - - `Roanoke, Virginia` - - `Cheyenne, Wyoming` - - `Birmingham, Alabama` - - `Mobile, Alabama` - - `Anchorage, Alaska` - - `Phoenix, Arizona` - - `Little Rock, Arkansas` - - `Los Angeles, California` - - `San Diego, California` - - `San Francisco, California` - - `Denver, Colorado` - - `Hartford, Connecticut` - - `Washington, District of Columbia` - - `Jacksonville, Florida` - - `Miami, Florida` - - `Tampa, Florida` - - `Atlanta, Georgia` - - `Honolulu, Hawaii` - - `Boise, Idaho` - - `Chicago, Illinois` - - `Indianapolis, Indiana` - - `Des Moines, Iowa` - - `Louisville, Kentucky` - - `New Orleans, Louisiana` - - `Baltimore, Maryland` - - `Boston, Massachusetts` - - `Detroit, Michigan` - - `St. Paul, Minnesota` - - `Jackson, Mississippi` - - `Kansas City, Missouri` - - `St. Louis, Missouri` - - `Helena, Montana` - - `Omaha, Nebraska` - - `Las Vegas, Nevada` - - `Reno, Nevada` - - `Albuquerque, New Mexico` - - `Buffalo, New York` - - `New York City, New York` - - `Winston-Salem, North Carolina` - - `Cincinnati, Ohio` - - `Cleveland, Ohio` - - `Columbus, Ohio` - - `Oklahoma City, Oklahoma` - - `Portland, Oregon` - - `Philadelphia, Pennsylvania` - - `Pittsburgh, Pennsylvania` - - `Columbia, South Carolina` - - `Knoxville, Tennessee` - - `Memphis, Tennessee` - - `Nashville, Tennessee` - - `Dallas, Texas` - - `El Paso, Texas` - - `Houston, Texas` - - `Lubbock, Texas` - - `San Antonio, Texas` - - `Salt Lake City, Utah` - - `Richmond, Virginia` - - `Seattle, Washington` - - `Spokane, Washington` - - `Charleston, West Virginia` - - `Milwaukee, Wisconsin` - - `null` - -#### Condition #2 for `preferredTrialCity`: - -> `string` - -##### Regex Pattern - - -`/^[a-zA-Z ]+, [a-zA-Z ]+, [0-9]+$/` - -### privatePractitioners - - -List of private practitioners associated with the case. - -> `array` | optional - -### procedureType - - -Procedure type of the case. - -> `string` | required - -##### Allowed Values - - - - `Regular` - - `Small` - -### qcCompleteForTrial - - -QC Checklist object that must be completed before the case can go to trial. - - -Restricted - -> `object` | optional - -### receivedAt - - -When the case was received by the court. If electronic, this value will be the same as createdAt. If paper, this value can be edited. - -> `date` | required - -### sealedDate - - -When the case was sealed from the public. - -> `date` | optional - -##### Can be null. - -### sortableDocketNumber - - -A sortable representation of the docket number (auto-generated by constructor). - -> `number` | required - -### statistics - - -List of Statistic Entities for the case. - -> `array` | optional - - -An array of [`Statistic`](./Statistic.md)s - -### status - - -Status of the case. - - -Restricted - -> `string` | optional - -##### Allowed Values - - - - `Assigned - Case` - - `Assigned - Motion` - - `Calendared` - - `CAV` - - `Closed` - - `General Docket - Not at Issue` - - `General Docket - At Issue (Ready for Trial)` - - `Jurisdiction Retained` - - `New` - - `On Appeal` - - `Rule 155` - - `Submitted` - -### closedDate - -> `any` - - -If `status` = `Closed`, then this field is `date` and is `required.` - - -Otherwise, this field is `any` and is `optional`. `null` is allowed. - -### trialDate - - -When this case goes to trial. - -> `date` | optional - -##### Can be null. - -### trialLocation - - -Where this case goes to trial. This may be different that the preferred trial location. - -> `conditional` | optional - - -*Must match 1 of the following conditions:* - -#### Condition #1 for `trialLocation`: - -> `string` - -##### Allowed Values - - - - `Fresno, California` - - `Tallahassee, Florida` - - `Pocatello, Idaho` - - `Peoria, Illinois` - - `Wichita, Kansas` - - `Shreveport, Louisiana` - - `Portland, Maine` - - `Billings, Montana` - - `Albany, New York` - - `Syracuse, New York` - - `Bismarck, North Dakota` - - `Aberdeen, South Dakota` - - `Burlington, Vermont` - - `Roanoke, Virginia` - - `Cheyenne, Wyoming` - - `Birmingham, Alabama` - - `Mobile, Alabama` - - `Anchorage, Alaska` - - `Phoenix, Arizona` - - `Little Rock, Arkansas` - - `Los Angeles, California` - - `San Diego, California` - - `San Francisco, California` - - `Denver, Colorado` - - `Hartford, Connecticut` - - `Washington, District of Columbia` - - `Jacksonville, Florida` - - `Miami, Florida` - - `Tampa, Florida` - - `Atlanta, Georgia` - - `Honolulu, Hawaii` - - `Boise, Idaho` - - `Chicago, Illinois` - - `Indianapolis, Indiana` - - `Des Moines, Iowa` - - `Louisville, Kentucky` - - `New Orleans, Louisiana` - - `Baltimore, Maryland` - - `Boston, Massachusetts` - - `Detroit, Michigan` - - `St. Paul, Minnesota` - - `Jackson, Mississippi` - - `Kansas City, Missouri` - - `St. Louis, Missouri` - - `Helena, Montana` - - `Omaha, Nebraska` - - `Las Vegas, Nevada` - - `Reno, Nevada` - - `Albuquerque, New Mexico` - - `Buffalo, New York` - - `New York City, New York` - - `Winston-Salem, North Carolina` - - `Cincinnati, Ohio` - - `Cleveland, Ohio` - - `Columbus, Ohio` - - `Oklahoma City, Oklahoma` - - `Portland, Oregon` - - `Philadelphia, Pennsylvania` - - `Pittsburgh, Pennsylvania` - - `Columbia, South Carolina` - - `Knoxville, Tennessee` - - `Memphis, Tennessee` - - `Nashville, Tennessee` - - `Dallas, Texas` - - `El Paso, Texas` - - `Houston, Texas` - - `Lubbock, Texas` - - `San Antonio, Texas` - - `Salt Lake City, Utah` - - `Richmond, Virginia` - - `Seattle, Washington` - - `Spokane, Washington` - - `Charleston, West Virginia` - - `Milwaukee, Wisconsin` - - `null` - -#### Condition #2 for `trialLocation`: - -> `string` - -##### Regex Pattern - - -`/^[a-zA-Z ]+, [a-zA-Z ]+, [0-9]+$/` - -### trialSessionId - - -The unique ID of the trial session associated with this case. - -> `string` | optional - -### trialTime - - -Time of day when this case goes to trial. - -> `string` | optional - -##### Regex Pattern - - -`/^[0-9]{1,2}:([0-5][0-9])$/` - -### useSameAsPrimary - - -Whether to use the same address for the primary and secondary petitioner contact information (used only in data entry and QC process). - -> `boolean` | optional - -### userId - - -The unique ID of the User who added the case to the system. - - -Restricted - -> `string` | optional - -##### Maximum limit - - -`50` - -### workItems - - -List of system messages associated with this case. - - -Restricted - -> `array` | optional + ``` +--- + type: "object" + keys: + associatedJudge: + type: "string" + flags: + presence: "optional" + description: "Judge assigned to this case. Defaults to Chief Judge." + rules: + - + name: "max" + args: + limit: 50 + metas: + - + tags: + - "Restricted" + automaticBlocked: + type: "boolean" + flags: + presence: "optional" + description: "Temporarily blocked from trial due to a pending item or due date." + automaticBlockedDate: + type: "any" + whens: + - + ref: + path: + - "automaticBlocked" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + automaticBlockedReason: + type: "any" + whens: + - + ref: + path: + - "automaticBlocked" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "string" + flags: + only: true + presence: "required" + description: "The reason the case was automatically blocked from trial." + allow: + - "Due Date" + - "Pending Item" + - "Pending Item and Due Date" + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + blocked: + type: "boolean" + flags: + presence: "optional" + description: "Temporarily blocked from trial." + metas: + - + tags: + - "Restricted" + blockedDate: + type: "any" + metas: + - + tags: + - "Restricted" + whens: + - + ref: + path: + - "blocked" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + blockedReason: + type: "any" + metas: + - + tags: + - "Restricted" + whens: + - + ref: + path: + - "blocked" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "string" + flags: + presence: "required" + description: "Open text field for describing reason for blocking this case from trial." + rules: + - + name: "max" + args: + limit: 250 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + caseCaption: + type: "string" + flags: + presence: "required" + description: "The name of the party bringing the case, e.g. \"Carol Williams, Petitioner,\" \"Mark Taylor, Incompetent, Debra Thomas, Next Friend, Petitioner,\" or \"Estate of Test Taxpayer, Deceased, Petitioner.\" This is the first half of the case title." + rules: + - + name: "max" + args: + limit: 500 + caseId: + type: "string" + flags: + presence: "required" + description: "Unique case ID only used by the system." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + caseNote: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + metas: + - + tags: + - "Restricted" + caseType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "CDP (Lien/Levy)" + - "Deficiency" + - "Declaratory Judgment (Exempt Organization)" + - "Declaratory Judgment (Retirement Plan)" + - "Innocent Spouse" + - "Interest Abatement" + - "Other" + - "Partnership (BBA Section 1101)" + - "Partnership (Section 6226)" + - "Partnership (Section 6228)" + - "Passport" + - "Whistleblower" + - "Worker Classification" + contactPrimary: + type: "object" + flags: + presence: "required" + contactSecondary: + type: "object" + flags: + presence: "optional" + allow: + - null + correspondence: + type: "array" + flags: + description: "List of Correspondence documents for the case." + items: + - + type: "object" + metas: + - + entityName: "Correspondence" + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "When the paper or electronic case was added to the system. This value cannot be edited." + damages: + type: "number" + flags: + presence: "optional" + description: "Damages for the case." + allow: + - null + docketNumber: + type: "string" + flags: + presence: "required" + description: "Unique case ID in XXXXX-YY format." + rules: + - + name: "pattern" + args: + regex: "/^([1-9]\\d{2,4}-\\d{2})$/" + docketNumberSuffix: + type: "string" + flags: + only: true + presence: "optional" + allow: + - null + - "W" + - "P" + - "X" + - "R" + - "SL" + - "L" + - "S" + docketNumberWithSuffix: + type: "string" + flags: + presence: "optional" + description: "Auto-generated from docket number and the suffix." + docketRecord: + type: "array" + flags: + presence: "required" + description: "List of DocketRecord Entities for the case." + rules: + - + name: "unique" + args: + comparator: [object Function] + items: + - + type: "object" + metas: + - + entityName: "DocketRecord" + documents: + type: "array" + flags: + presence: "required" + description: "List of Document Entities for the case." + items: + - + type: "object" + metas: + - + entityName: "Document" + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Case" + filingType: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Myself" + - "Myself and my spouse" + - "A business" + - "Other" + - "Individual petitioner" + - "Petitioner and spouse" + hasVerifiedIrsNotice: + type: "boolean" + flags: + presence: "optional" + description: "Whether the petitioner received an IRS notice, verified by the petitions clerk." + allow: + - null + highPriority: + type: "boolean" + flags: + presence: "optional" + metas: + - + tags: + - "Restricted" + highPriorityReason: + type: "any" + metas: + - + tags: + - "Restricted" + whens: + - + ref: + path: + - "highPriority" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 250 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + initialCaption: + type: "string" + flags: + presence: "optional" + description: "Case caption before modification." + rules: + - + name: "max" + args: + limit: 500 + allow: + - null + initialDocketNumberSuffix: + type: "string" + flags: + presence: "optional" + description: "Case docket number suffix before modification." + rules: + - + name: "max" + args: + limit: 2 + allow: + - null + irsNoticeDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + description: "Last date that the petitioner is allowed to file before." + rules: + - + name: "max" + args: + date: "now" + allow: + - null + irsPractitioners: + type: "array" + flags: + presence: "optional" + description: "List of IRS practitioners (also known as respondents) associated with the case." + isPaper: + type: "boolean" + flags: + presence: "optional" + leadCaseId: + type: "string" + flags: + presence: "optional" + description: "If this case is consolidated, this is the ID of the lead case. It is the lowest docket number in the consolidated group." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + litigationCosts: + type: "number" + flags: + presence: "optional" + description: "Litigation costs for the case." + allow: + - null + mailingDate: + type: "any" + flags: + description: "Date that petition was mailed to the court." + whens: + - + ref: + path: + - "isPaper" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 25 + otherwise: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 25 + allow: + - null + noticeOfAttachments: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the notice of attachments." + noticeOfTrialDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + description: "Reminder for clerks to review the notice of trial date." + orderDesignatingPlaceOfTrial: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the Order Designating Place of Trial." + orderForAmendedPetition: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the order for amended Petition." + orderForAmendedPetitionAndFilingFee: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the order for amended Petition And filing fee." + orderForFilingFee: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the order for filing fee." + orderForOds: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the order for ODS." + orderForRatification: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the Order for Ratification." + orderToChangeDesignatedPlaceOfTrial: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the Order to Change Designated Place Of Trial." + orderToShowCause: + type: "boolean" + flags: + presence: "optional" + description: "Reminder for clerks to review the Order to Show Cause." + partyType: + type: "string" + flags: + only: true + presence: "required" + description: "Party type of the case petitioner." + allow: + - "Conservator" + - "Corporation" + - "Custodian" + - "Donor" + - "Estate with an executor/personal representative/fiduciary/etc." + - "Estate without an executor/personal representative/fiduciary/etc." + - "Guardian" + - "Next friend for a legally incompetent person (without a guardian, conservator, or other like fiduciary)" + - "Next friend for a minor (without a guardian, conservator, or other like fiduciary)" + - "Partnership (as the Tax Matters Partner)" + - "Partnership (as a partnership representative under the BBA regime)" + - "Partnership (as a partner other than Tax Matters Partner)" + - "Petitioner" + - "Petitioner & deceased spouse" + - "Petitioner & spouse" + - "Surviving spouse" + - "Transferee" + - "Trust" + petitionPaymentStatus: + type: "string" + flags: + only: true + presence: "required" + description: "Status of the case fee payment." + allow: + - "Paid" + - "Not Paid" + - "Waived" + petitionPaymentDate: + type: "any" + flags: + description: "When the petitioner paid the case fee." + whens: + - + ref: + path: + - "petitionPaymentStatus" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "Paid" + then: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + otherwise: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + allow: + - null + petitionPaymentMethod: + type: "any" + flags: + description: "How the petitioner paid the case fee." + whens: + - + ref: + path: + - "petitionPaymentStatus" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "Paid" + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 50 + otherwise: + type: "string" + flags: + presence: "optional" + allow: + - null + petitionPaymentWaivedDate: + type: "any" + flags: + description: "When the case fee was waived." + whens: + - + ref: + path: + - "petitionPaymentStatus" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "Waived" + then: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + otherwise: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + allow: + - null + preferredTrialCity: + type: "alternatives" + flags: + presence: "optional" + description: "Where the petitioner would prefer to hold the case trial." + matches: + - + schema: + type: "string" + flags: + only: true + allow: + - "Fresno, California" + - "Tallahassee, Florida" + - "Pocatello, Idaho" + - "Peoria, Illinois" + - "Wichita, Kansas" + - "Shreveport, Louisiana" + - "Portland, Maine" + - "Billings, Montana" + - "Albany, New York" + - "Syracuse, New York" + - "Bismarck, North Dakota" + - "Aberdeen, South Dakota" + - "Burlington, Vermont" + - "Roanoke, Virginia" + - "Cheyenne, Wyoming" + - "Birmingham, Alabama" + - "Mobile, Alabama" + - "Anchorage, Alaska" + - "Phoenix, Arizona" + - "Little Rock, Arkansas" + - "Los Angeles, California" + - "San Diego, California" + - "San Francisco, California" + - "Denver, Colorado" + - "Hartford, Connecticut" + - "Washington, District of Columbia" + - "Jacksonville, Florida" + - "Miami, Florida" + - "Tampa, Florida" + - "Atlanta, Georgia" + - "Honolulu, Hawaii" + - "Boise, Idaho" + - "Chicago, Illinois" + - "Indianapolis, Indiana" + - "Des Moines, Iowa" + - "Louisville, Kentucky" + - "New Orleans, Louisiana" + - "Baltimore, Maryland" + - "Boston, Massachusetts" + - "Detroit, Michigan" + - "St. Paul, Minnesota" + - "Jackson, Mississippi" + - "Kansas City, Missouri" + - "St. Louis, Missouri" + - "Helena, Montana" + - "Omaha, Nebraska" + - "Las Vegas, Nevada" + - "Reno, Nevada" + - "Albuquerque, New Mexico" + - "Buffalo, New York" + - "New York City, New York" + - "Winston-Salem, North Carolina" + - "Cincinnati, Ohio" + - "Cleveland, Ohio" + - "Columbus, Ohio" + - "Oklahoma City, Oklahoma" + - "Portland, Oregon" + - "Philadelphia, Pennsylvania" + - "Pittsburgh, Pennsylvania" + - "Columbia, South Carolina" + - "Knoxville, Tennessee" + - "Memphis, Tennessee" + - "Nashville, Tennessee" + - "Dallas, Texas" + - "El Paso, Texas" + - "Houston, Texas" + - "Lubbock, Texas" + - "San Antonio, Texas" + - "Salt Lake City, Utah" + - "Richmond, Virginia" + - "Seattle, Washington" + - "Spokane, Washington" + - "Charleston, West Virginia" + - "Milwaukee, Wisconsin" + - null + - + schema: + type: "string" + rules: + - + name: "pattern" + args: + regex: "/^[a-zA-Z ]+, [a-zA-Z ]+, [0-9]+$/" + privatePractitioners: + type: "array" + flags: + presence: "optional" + description: "List of private practitioners associated with the case." + procedureType: + type: "string" + flags: + only: true + presence: "required" + description: "Procedure type of the case." + allow: + - "Regular" + - "Small" + qcCompleteForTrial: + type: "object" + flags: + presence: "optional" + description: "QC Checklist object that must be completed before the case can go to trial." + metas: + - + tags: + - "Restricted" + receivedAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "When the case was received by the court. If electronic, this value will be the same as createdAt. If paper, this value can be edited." + sealedDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + description: "When the case was sealed from the public." + allow: + - null + sortableDocketNumber: + type: "number" + flags: + presence: "required" + description: "A sortable representation of the docket number (auto-generated by constructor)." + statistics: + type: "any" + flags: + description: "List of Statistic Entities for the case." + whens: + - + ref: + path: + - "hasVerifiedIrsNotice" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "any" + whens: + - + ref: + path: + - "caseType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "Deficiency" + then: + type: "array" + flags: + presence: "required" + rules: + - + name: "min" + args: + limit: 1 + items: + - + type: "object" + metas: + - + entityName: "Statistic" + otherwise: + type: "array" + flags: + presence: "optional" + items: + - + type: "object" + metas: + - + entityName: "Statistic" + otherwise: + type: "array" + flags: + presence: "optional" + items: + - + type: "object" + metas: + - + entityName: "Statistic" + status: + type: "string" + flags: + only: true + presence: "optional" + description: "Status of the case." + allow: + - "Assigned - Case" + - "Assigned - Motion" + - "Calendared" + - "CAV" + - "Closed" + - "General Docket - Not at Issue" + - "General Docket - At Issue (Ready for Trial)" + - "Jurisdiction Retained" + - "New" + - "On Appeal" + - "Rule 155" + - "Submitted" + metas: + - + tags: + - "Restricted" + closedDate: + type: "any" + whens: + - + ref: + path: + - "status" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "Closed" + then: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + trialDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + description: "When this case goes to trial." + allow: + - null + trialLocation: + type: "alternatives" + flags: + presence: "optional" + description: "Where this case goes to trial. This may be different that the preferred trial location." + matches: + - + schema: + type: "string" + flags: + only: true + allow: + - "Fresno, California" + - "Tallahassee, Florida" + - "Pocatello, Idaho" + - "Peoria, Illinois" + - "Wichita, Kansas" + - "Shreveport, Louisiana" + - "Portland, Maine" + - "Billings, Montana" + - "Albany, New York" + - "Syracuse, New York" + - "Bismarck, North Dakota" + - "Aberdeen, South Dakota" + - "Burlington, Vermont" + - "Roanoke, Virginia" + - "Cheyenne, Wyoming" + - "Birmingham, Alabama" + - "Mobile, Alabama" + - "Anchorage, Alaska" + - "Phoenix, Arizona" + - "Little Rock, Arkansas" + - "Los Angeles, California" + - "San Diego, California" + - "San Francisco, California" + - "Denver, Colorado" + - "Hartford, Connecticut" + - "Washington, District of Columbia" + - "Jacksonville, Florida" + - "Miami, Florida" + - "Tampa, Florida" + - "Atlanta, Georgia" + - "Honolulu, Hawaii" + - "Boise, Idaho" + - "Chicago, Illinois" + - "Indianapolis, Indiana" + - "Des Moines, Iowa" + - "Louisville, Kentucky" + - "New Orleans, Louisiana" + - "Baltimore, Maryland" + - "Boston, Massachusetts" + - "Detroit, Michigan" + - "St. Paul, Minnesota" + - "Jackson, Mississippi" + - "Kansas City, Missouri" + - "St. Louis, Missouri" + - "Helena, Montana" + - "Omaha, Nebraska" + - "Las Vegas, Nevada" + - "Reno, Nevada" + - "Albuquerque, New Mexico" + - "Buffalo, New York" + - "New York City, New York" + - "Winston-Salem, North Carolina" + - "Cincinnati, Ohio" + - "Cleveland, Ohio" + - "Columbus, Ohio" + - "Oklahoma City, Oklahoma" + - "Portland, Oregon" + - "Philadelphia, Pennsylvania" + - "Pittsburgh, Pennsylvania" + - "Columbia, South Carolina" + - "Knoxville, Tennessee" + - "Memphis, Tennessee" + - "Nashville, Tennessee" + - "Dallas, Texas" + - "El Paso, Texas" + - "Houston, Texas" + - "Lubbock, Texas" + - "San Antonio, Texas" + - "Salt Lake City, Utah" + - "Richmond, Virginia" + - "Seattle, Washington" + - "Spokane, Washington" + - "Charleston, West Virginia" + - "Milwaukee, Wisconsin" + - null + - + schema: + type: "string" + rules: + - + name: "pattern" + args: + regex: "/^[a-zA-Z ]+, [a-zA-Z ]+, [0-9]+$/" + trialSessionId: + type: "string" + flags: + presence: "optional" + description: "The unique ID of the trial session associated with this case." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + trialTime: + type: "string" + flags: + presence: "optional" + description: "Time of day when this case goes to trial." + rules: + - + name: "pattern" + args: + regex: "/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/" + useSameAsPrimary: + type: "boolean" + flags: + presence: "optional" + description: "Whether to use the same address for the primary and secondary petitioner contact information (used only in data entry and QC process)." + userId: + type: "string" + flags: + presence: "optional" + description: "The unique ID of the User who added the case to the system." + rules: + - + name: "max" + args: + limit: 50 + metas: + - + tags: + - "Restricted" + workItems: + type: "array" + flags: + presence: "optional" + description: "List of system messages associated with this case." + metas: + - + tags: + - "Restricted" + + ``` diff --git a/docs/entities/CaseDeadline.md b/docs/entities/CaseDeadline.md index 110b63e438a..fa7e6036b2d 100644 --- a/docs/entities/CaseDeadline.md +++ b/docs/entities/CaseDeadline.md @@ -1,52 +1,68 @@ # CaseDeadline - -### caseDeadlineId - - -Unique Case Deadline ID only used by the system. - -> `string` | required - -### caseId - - -Unique Case ID only used by the system. - -> `string` | required - -### createdAt - - -When the Case Deadline was added to the system. - -> `date` | required - -### deadlineDate - - -When the Case Deadline expires. - -> `date` | required - -### description - - -User provided description of the Case Deadline. - -> `string` | required - -##### Maximum limit - - -`120` - -##### Minimum limit - - -`1` - -### entityName - -> `string` | required - -##### Can be CaseDeadline. + ``` +--- + type: "object" + keys: + caseDeadlineId: + type: "string" + flags: + presence: "required" + description: "Unique Case Deadline ID only used by the system." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + caseId: + type: "string" + flags: + presence: "required" + description: "Unique Case ID only used by the system." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "When the Case Deadline was added to the system." + deadlineDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "When the Case Deadline expires." + description: + type: "string" + flags: + presence: "required" + description: "User provided description of the Case Deadline." + rules: + - + name: "max" + args: + limit: 120 + - + name: "min" + args: + limit: 1 + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "CaseDeadline" + + ``` diff --git a/docs/entities/CaseMessage.md b/docs/entities/CaseMessage.md new file mode 100644 index 00000000000..defe0a48486 --- /dev/null +++ b/docs/entities/CaseMessage.md @@ -0,0 +1,225 @@ +# CaseMessage + ``` +--- + type: "object" + keys: + caseId: + type: "string" + flags: + presence: "required" + description: "ID of the case the message is attached to." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + caseStatus: + type: "string" + flags: + presence: "optional" + description: "The status of the associated case." + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "When the message was created." + docketNumber: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^([1-9]\\d{2,4}-\\d{2})$/" + docketNumberWithSuffix: + type: "string" + flags: + presence: "optional" + description: "The docket number and suffix for the associated case." + allow: + - null + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "CaseMessage" + from: + type: "string" + flags: + presence: "required" + description: "The name of the user who sent the message." + rules: + - + name: "max" + args: + limit: 100 + fromSection: + type: "string" + flags: + only: true + presence: "required" + description: "The section of the user who sent the message." + allow: + - "adc" + - "admissions" + - "chambers" + - "clerkofcourt" + - "docket" + - "petitions" + - "trialClerks" + - "armensChambers" + - "ashfordsChambers" + - "buchsChambers" + - "carluzzosChambers" + - "cohensChambers" + - "colvinsChambers" + - "copelandsChambers" + - "foleysChambers" + - "galesChambers" + - "gerbersChambers" + - "goekesChambers" + - "gustafsonsChambers" + - "guysChambers" + - "halpernsChambers" + - "holmesChambers" + - "jacobsChambers" + - "jonesChambers" + - "kerrigansChambers" + - "laubersChambers" + - "leydensChambers" + - "marvelsChambers" + - "morrisonsChambers" + - "negasChambers" + - "panuthosChambers" + - "parisChambers" + - "pughsChambers" + - "ruwesChambers" + - "thorntonsChambers" + - "urdasChambers" + - "vasquezsChambers" + - "wellsChambers" + fromUserId: + type: "string" + flags: + presence: "required" + description: "The ID of the user who sent the message." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + message: + type: "string" + flags: + presence: "required" + description: "The message text." + rules: + - + name: "max" + args: + limit: 500 + messageId: + type: "string" + flags: + presence: "required" + description: "A unique ID generated by the system to represent the message." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + subject: + type: "string" + flags: + presence: "required" + description: "The subject line of the message." + rules: + - + name: "max" + args: + limit: 250 + to: + type: "string" + flags: + presence: "required" + description: "The name of the user who is the recipient of the message." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + toSection: + type: "string" + flags: + only: true + presence: "required" + description: "The section of the user who is the recipient of the message." + allow: + - "adc" + - "admissions" + - "chambers" + - "clerkofcourt" + - "docket" + - "petitions" + - "trialClerks" + - "armensChambers" + - "ashfordsChambers" + - "buchsChambers" + - "carluzzosChambers" + - "cohensChambers" + - "colvinsChambers" + - "copelandsChambers" + - "foleysChambers" + - "galesChambers" + - "gerbersChambers" + - "goekesChambers" + - "gustafsonsChambers" + - "guysChambers" + - "halpernsChambers" + - "holmesChambers" + - "jacobsChambers" + - "jonesChambers" + - "kerrigansChambers" + - "laubersChambers" + - "leydensChambers" + - "marvelsChambers" + - "morrisonsChambers" + - "negasChambers" + - "panuthosChambers" + - "parisChambers" + - "pughsChambers" + - "ruwesChambers" + - "thorntonsChambers" + - "urdasChambers" + - "vasquezsChambers" + - "wellsChambers" + toUserId: + type: "string" + flags: + presence: "required" + description: "The ID of the user who is the recipient of the message." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + + ``` diff --git a/docs/entities/Correspondence.md b/docs/entities/Correspondence.md new file mode 100644 index 00000000000..56309ec437a --- /dev/null +++ b/docs/entities/Correspondence.md @@ -0,0 +1,62 @@ +# Correspondence + ``` +--- + type: "object" + keys: + documentId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + documentTitle: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 500 + filedBy: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + allow: + - "" + filingDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "Date that this Document was filed." + rules: + - + name: "max" + args: + date: "now" + userId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + + ``` diff --git a/docs/entities/DocketRecord.md b/docs/entities/DocketRecord.md index 3384831688e..d4c5ebbbf94 100644 --- a/docs/entities/DocketRecord.md +++ b/docs/entities/DocketRecord.md @@ -1,436 +1,468 @@ # DocketRecord - -### action - - -Action taken in response to this Docket Record item. - -> `string` | optional - -##### Can be null. - -### description - - -Text that describes this Docket Record item, which may be part of the Filings and Proceedings value. - -> `string` | required - -### documentId - - -ID of the associated PDF document in the S3 bucket. - -> `string` | optional - -##### Can be null. - -### editState - - -JSON representation of the in-progress edit of this item. - - -Restricted - -> `string` | optional - -##### Can be null. - -### entityName - -> `string` | required - -##### Can be DocketRecord. - -### eventCode - - -Code associated with the event that resulted in this item being added to the Docket Record. - -> `string` | required - -##### Allowed Values - - - - `A` - - `AAAP` - - `AAPN` - - `AATP` - - `AATS` - - `AATT` - - `ACED` - - `ADMR` - - `ADMT` - - `AFE` - - `AFF` - - `AMAT` - - `AMDC` - - `APA` - - `APLD` - - `APPL` - - `APPW` - - `APW` - - `ASAP` - - `ASUP` - - `ATAP` - - `ATSP` - - `BND` - - `BRF` - - `CERT` - - `CIVP` - - `COED` - - `CS` - - `CTRA` - - `DCL` - - `DEC` - - `DISC` - - `DSC` - - `EA` - - `ES` - - `EVID` - - `EXH` - - `FEE` - - `FEEW` - - `FTRL` - - `HE` - - `HEAR` - - `LTR` - - `M000` - - `M001` - - `M002` - - `M003` - - `M004` - - `M005` - - `M006` - - `M007` - - `M008` - - `M009` - - `M010` - - `M011` - - `M012` - - `M013` - - `M014` - - `M015` - - `M016` - - `M017` - - `M018` - - `M019` - - `M020` - - `M021` - - `M022` - - `M023` - - `M024` - - `M026` - - `M027` - - `M028` - - `M029` - - `M030` - - `M031` - - `M032` - - `M033` - - `M034` - - `M035` - - `M036` - - `M037` - - `M038` - - `M039` - - `M040` - - `M041` - - `M042` - - `M043` - - `M044` - - `M045` - - `M046` - - `M047` - - `M048` - - `M049` - - `M050` - - `M051` - - `M052` - - `M053` - - `M054` - - `M055` - - `M056` - - `M057` - - `M058` - - `M059` - - `M060` - - `M061` - - `M062` - - `M063` - - `M064` - - `M065` - - `M066` - - `M067` - - `M068` - - `M069` - - `M070` - - `M071` - - `M072` - - `M073` - - `M074` - - `M075` - - `M076` - - `M077` - - `M078` - - `M079` - - `M080` - - `M081` - - `M082` - - `M083` - - `M084` - - `M085` - - `M086` - - `M087` - - `M088` - - `M089` - - `M090` - - `M091` - - `M092` - - `M093` - - `M094` - - `M095` - - `M096` - - `M097` - - `M098` - - `M099` - - `M100` - - `M101` - - `M102` - - `M103` - - `M104` - - `M105` - - `M106` - - `M107` - - `M108` - - `M109` - - `M110` - - `M111` - - `M112` - - `M113` - - `M114` - - `M115` - - `M116` - - `M117` - - `M118` - - `M119` - - `M120` - - `M121` - - `M122` - - `M123` - - `M124` - - `M125` - - `M126` - - `M129` - - `M130` - - `M131` - - `M132` - - `M133` - - `M134` - - `M135` - - `M136` - - `M218` - - `MEMO` - - `MGRTED` - - `MINC` - - `MIND` - - `MISC` - - `MISCL` - - `MISL` - - `MISP` - - `MOP` - - `NAJA` - - `NCA` - - `NCAG` - - `NCAP` - - `NCNP` - - `NCON` - - `NCP` - - `NCTP` - - `NDC` - - `NDT` - - `NFAR` - - `NIFL` - - `NINF` - - `NIS` - - `NITM` - - `NJAR` - - `NNOB` - - `NOA` - - `NOB` - - `NODC` - - `NOEI` - - `NOEP` - - `NOI` - - `NOST` - - `NOT` - - `NOU` - - `NPB` - - `NPJR` - - `NRJD` - - `NRJR` - - `NSA` - - `NSTE` - - `NTA` - - `NTD` - - `NTN` - - `O` - - `OAD` - - `OAJ` - - `OAL` - - `OAP` - - `OAPF` - - `OAR` - - `OAS` - - `OASL` - - `OAW` - - `OAX` - - `OBJ` - - `OBJE` - - `OBJN` - - `OCA` - - `OD` - - `ODD` - - `ODJ` - - `ODL` - - `ODP` - - `ODR` - - `ODS` - - `ODSL` - - `ODW` - - `ODX` - - `OF` - - `OFAB` - - `OFFX` - - `OFWD` - - `OFX` - - `OIP` - - `OJR` - - `OODS` - - `OP` - - `OPFX` - - `OPPO` - - `OPX` - - `ORAP` - - `OROP` - - `OSC` - - `OSCP` - - `OST` - - `OSUB` - - `P` - - `PARD` - - `PHM` - - `PMT` - - `PSDE` - - `PTFR` - - `PTRL` - - `RAT` - - `RATF` - - `RCOM` - - `REDC` - - `REPL` - - `REQ` - - `REQA` - - `RESP` - - `RFPC` - - `RJN` - - `RLRI` - - `RM` - - `ROA` - - `RPT` - - `RQT` - - `RSP` - - `RTP` - - `RTRA` - - `S212` - - `SADM` - - `SAMB` - - `SATL` - - `SDEC` - - `SEAB` - - `SEOB` - - `SERB` - - `SESB` - - `SIAB` - - `SIAM` - - `SIMB` - - `SIML` - - `SIOB` - - `SIOM` - - `SIRB` - - `SISB` - - `SOC` - - `SOMB` - - `SOP` - - `SORI` - - `SPAR` - - `SPD` - - `SPML` - - `SPMT` - - `SPTN` - - `SPTO` - - `SRMB` - - `SSB` - - `SSRB` - - `SSRM` - - `SSTP` - - `STAR` - - `STAT` - - `STBB` - - `STIN` - - `STIP` - - `STP` - - `STPD` - - `STS` - - `STST` - - `SUPM` - - `SURP` - - `Standard` - - `TCOP` - - `TE` - - `TRAN` - - `TRL` - - `USCA` - - `USDL` - - `WRIT` - -### filedBy - - -ID of the user that filed this Docket Record item. - - -Restricted - -> `string` | optional - -##### Can be null. - -### filingDate - - -Date that this Docket Record item was filed. - -> `date` | required - -##### Maximum date - - -`now` - -### index - - -Index of this item in the Docket Record list. - -> `number` | required - -### servedPartiesCode - - -Served parties code to override system-computed code. - -> `string` | optional - -##### Can be null. + ``` +--- + type: "object" + keys: + action: + type: "string" + flags: + presence: "optional" + description: "Action taken in response to this Docket Record item." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + description: + type: "string" + flags: + presence: "required" + description: "Text that describes this Docket Record item, which may be part of the Filings and Proceedings value." + rules: + - + name: "max" + args: + limit: 500 + documentId: + type: "string" + flags: + presence: "optional" + description: "ID of the associated PDF document in the S3 bucket." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + editState: + type: "string" + flags: + presence: "optional" + description: "JSON representation of the in-progress edit of this item." + rules: + - + name: "max" + args: + limit: 1000 + allow: + - null + metas: + - + tags: + - "Restricted" + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "DocketRecord" + eventCode: + type: "string" + flags: + only: true + presence: "required" + description: "Code associated with the event that resulted in this item being added to the Docket Record." + allow: + - "A" + - "AAAP" + - "AAPN" + - "AATP" + - "AATS" + - "AATT" + - "ACED" + - "ADMR" + - "ADMT" + - "AFE" + - "AFF" + - "AFP" + - "AMAT" + - "AMDC" + - "APA" + - "APLD" + - "APPL" + - "APPW" + - "APW" + - "ASAP" + - "ASUP" + - "ATAP" + - "ATSP" + - "BND" + - "BRF" + - "CERT" + - "CIVP" + - "COED" + - "CS" + - "CTRA" + - "DCL" + - "DEC" + - "DISC" + - "DSC" + - "EA" + - "ES" + - "EVID" + - "EXH" + - "FEE" + - "FEEW" + - "FTRL" + - "HE" + - "HEAR" + - "LTR" + - "M000" + - "M001" + - "M002" + - "M003" + - "M004" + - "M005" + - "M006" + - "M007" + - "M008" + - "M009" + - "M010" + - "M011" + - "M012" + - "M013" + - "M014" + - "M015" + - "M016" + - "M017" + - "M018" + - "M019" + - "M020" + - "M021" + - "M022" + - "M023" + - "M024" + - "M026" + - "M027" + - "M028" + - "M029" + - "M030" + - "M031" + - "M032" + - "M033" + - "M034" + - "M035" + - "M036" + - "M037" + - "M038" + - "M039" + - "M040" + - "M041" + - "M042" + - "M043" + - "M044" + - "M045" + - "M046" + - "M047" + - "M048" + - "M049" + - "M050" + - "M051" + - "M052" + - "M053" + - "M054" + - "M055" + - "M056" + - "M057" + - "M058" + - "M059" + - "M060" + - "M061" + - "M062" + - "M063" + - "M064" + - "M065" + - "M066" + - "M067" + - "M068" + - "M069" + - "M070" + - "M071" + - "M072" + - "M073" + - "M074" + - "M075" + - "M076" + - "M077" + - "M078" + - "M079" + - "M080" + - "M081" + - "M082" + - "M083" + - "M084" + - "M085" + - "M086" + - "M087" + - "M088" + - "M089" + - "M090" + - "M091" + - "M092" + - "M093" + - "M094" + - "M095" + - "M096" + - "M097" + - "M098" + - "M099" + - "M100" + - "M101" + - "M102" + - "M103" + - "M104" + - "M105" + - "M106" + - "M107" + - "M108" + - "M109" + - "M110" + - "M111" + - "M112" + - "M113" + - "M114" + - "M115" + - "M116" + - "M117" + - "M118" + - "M119" + - "M120" + - "M121" + - "M122" + - "M123" + - "M124" + - "M125" + - "M126" + - "M129" + - "M130" + - "M131" + - "M132" + - "M133" + - "M134" + - "M135" + - "M136" + - "M218" + - "MEMO" + - "MGRTED" + - "MINC" + - "MIND" + - "MISC" + - "MISCL" + - "MISL" + - "MISP" + - "MOP" + - "NAJA" + - "NCA" + - "NCAG" + - "NCAP" + - "NCNP" + - "NCON" + - "NCP" + - "NCTP" + - "NDC" + - "NDT" + - "NFAR" + - "NIFL" + - "NINF" + - "NIS" + - "NITM" + - "NJAR" + - "NNOB" + - "NOA" + - "NOB" + - "NODC" + - "NOEI" + - "NOEP" + - "NOI" + - "NOST" + - "NOT" + - "NOU" + - "NPB" + - "NPJR" + - "NRJD" + - "NRJR" + - "NSA" + - "NSTE" + - "NTA" + - "NTD" + - "NTN" + - "O" + - "OAD" + - "OAJ" + - "OAL" + - "OAP" + - "OAPF" + - "OAR" + - "OAS" + - "OASL" + - "OAW" + - "OAX" + - "OBJ" + - "OBJE" + - "OBJN" + - "OCA" + - "OD" + - "ODD" + - "ODJ" + - "ODL" + - "ODP" + - "ODR" + - "ODS" + - "ODSL" + - "ODW" + - "ODX" + - "OF" + - "OFAB" + - "OFFX" + - "OFWD" + - "OFX" + - "OIP" + - "OJR" + - "OODS" + - "OP" + - "OPFX" + - "OPPO" + - "OPX" + - "ORAP" + - "OROP" + - "OSC" + - "OSCP" + - "OST" + - "OSUB" + - "P" + - "PARD" + - "PHM" + - "PMT" + - "PSDE" + - "PTFR" + - "PTRL" + - "RAT" + - "RATF" + - "RCOM" + - "REDC" + - "REPL" + - "REQ" + - "REQA" + - "RESP" + - "RFPC" + - "RJN" + - "RLRI" + - "RM" + - "ROA" + - "RPT" + - "RQT" + - "RSP" + - "RTP" + - "RTRA" + - "S212" + - "SADM" + - "SAMB" + - "SATL" + - "SDEC" + - "SEAB" + - "SEOB" + - "SERB" + - "SESB" + - "SIAB" + - "SIAM" + - "SIMB" + - "SIML" + - "SIOB" + - "SIOM" + - "SIRB" + - "SISB" + - "SOC" + - "SOMB" + - "SOP" + - "SORI" + - "SPAR" + - "SPD" + - "SPML" + - "SPMT" + - "SPTN" + - "SPTO" + - "SRMB" + - "SSB" + - "SSRB" + - "SSRM" + - "SSTP" + - "STAR" + - "STAT" + - "STBB" + - "STIN" + - "STIP" + - "STP" + - "STPD" + - "STS" + - "STST" + - "SUPM" + - "SURP" + - "TCOP" + - "TE" + - "TRAN" + - "TRL" + - "USCA" + - "USDL" + - "WRIT" + filedBy: + type: "string" + flags: + presence: "optional" + description: "User that filed this Docket Record item." + rules: + - + name: "max" + args: + limit: 500 + allow: + - null + metas: + - + tags: + - "Restricted" + filingDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "Date that this Docket Record item was filed." + rules: + - + name: "max" + args: + date: "now" + index: + type: "number" + flags: + presence: "required" + description: "Index of this item in the Docket Record list." + rules: + - + name: "integer" + numberOfPages: + type: "number" + flags: + presence: "optional" + allow: + - null + servedPartiesCode: + type: "string" + flags: + only: true + presence: "optional" + description: "Served parties code to override system-computed code." + allow: + - "R" + - "B" + - "" + - null + + ``` diff --git a/docs/entities/Document.md b/docs/entities/Document.md index db3ba9bee43..56c3a12a286 100644 --- a/docs/entities/Document.md +++ b/docs/entities/Document.md @@ -1,711 +1,804 @@ # Document - -### addToCoversheet - -> `boolean` | optional - -### additionalInfo - -> `string` | optional - -### additionalInfo2 - -> `string` | optional - -### archived - - -A document that was archived instead of added to the Docket Record. - -> `boolean` | optional - -### caseId - - -Unique ID of the associated Case. - -> `string` | optional - -### certificateOfService - -> `boolean` | optional - -### certificateOfServiceDate - -> `any` - - -If `certificateOfService` = `true`, then this field is `date` and is `required.` - - -Otherwise, this field is `any` and is `optional`. - -### createdAt - - -When the Document was added to the system. - -> `date` | required - -### date - - -An optional date used when generating a fully concatenated document title. - -> `date` | optional - -##### Can be null. - -### docketNumber - - -Docket Number of the associated Case in XXXXX-YY format. - -> `string` | optional - -##### Regex Pattern - - -`/^(\d{3,5}-\d{2})$/` - -### docketNumbers - - -Optional Docket Number text used when generating a fully concatenated document title. - -> `string` | optional - -### documentContentsId - - -The S3 ID containing the text contents of the document. - -> `string` | optional - -### documentId - - -ID of the associated PDF document in the S3 bucket. - -> `string` | required - -### documentTitle - - -The title of this document. - -> `string` | optional - -### documentType - - -The type of this document. - -> `string` | required - -##### Allowed Values - - - - `Application for Waiver of Filing Fee` - - `Ownership Disclosure Statement` - - `Petition` - - `Request for Place of Trial` - - `Statement of Taxpayer Identification` - - `Entry of Appearance` - - `Substitution of Counsel` - - `Answer` - - `Answer to Amended Petition` - - `Answer to Amended Petition, as Amended` - - `Answer to Amendment to Amended Petition` - - `Answer to Amendment to Petition` - - `Answer to Petition, as Amended` - - `Answer to Second Amended Petition` - - `Answer to Second Amendment to Petition` - - `Answer to Supplement to Petition` - - `Answer to Third Amended Petition` - - `Answer to Third Amendment to Petition` - - `Designation of Counsel to Receive Service` - - `Motion to Withdraw as Counsel` - - `Motion to Withdraw Counsel (filed by petitioner)` - - `Application for Waiver of Filing Fee and Affidavit` - - `Application to Take Deposition` - - `Agreed Computation for Entry of Decision` - - `Computation for Entry of Decision` - - `Proposed Stipulated Decision` - - `Revised Computation` - - `Administrative Record` - - `Amended` - - `Amended Certificate of Service` - - `Amendment [anything]` - - `Certificate as to the Genuineness of the Administrative Record` - - `Certificate of Service` - - `Civil Penalty Approval Form` - - `Exhibit(s)` - - `Memorandum` - - `Partial Administrative Record` - - `Ratification` - - `Redacted` - - `Report` - - `Status Report` - - `Motion for Continuance` - - `Motion for Extension of Time` - - `Motion to Dismiss for Lack of Jurisdiction` - - `Motion to Dismiss for Lack of Prosecution` - - `Motion for Summary Judgment` - - `Motion to Change or Correct Caption` - - `Motion for a New Trial` - - `Motion for an Order under Federal Rule of Evidence 502(d)` - - `Motion for an Order under Model Rule of Professional Conduct 4.2` - - `Motion for Appointment of Mediator` - - `Motion for Assignment of Judge` - - `Motion for Audio of Trial Proceeding(s)` - - `Motion for Certification of an Interlocutory Order to Permit Immediate Appeal` - - `Motion for Default and Dismissal` - - `Motion for Entry of Decision` - - `Motion for Entry of Order that Undenied Allegations be Deemed Admitted Pursuant to Rule 37(c)` - - `Motion for Estate Tax Deduction Developing at or after Trial Pursuant to Rule 156` - - `Motion for in Camera Review` - - `Motion for International Judicial Assistance` - - `Motion for Judgment on the Pleadings` - - `Motion for Leave to Conduct Discovery Pursuant to Rule 70(a)(2)` - - `Motion for Leave to File` - - `Motion for Leave to File Out of Time` - - `Motion for Leave to Serve Additional Interrogatories` - - `Motion for Leave to Use Electronic Equipment` - - `Motion for More Definite Statement Pursuant to Rule 51` - - `Motion for Non-Binding Mediation` - - `Motion for Oral Argument` - - `Motion for Order Fixing Amount of an Appeal Bond` - - `Motion for Order to Release the Amount of an Appeal Bond` - - `Motion for Order to Show Cause Why Case Should Not Be Sumitted on the Basis of the Administrative Record` - - `Motion for Order to Show Cause Why Judgment Should Not be Entered on the Basis of a Previously Decided Case` - - `Motion for Order to Show Cause Why Proposed Facts and Evidence Should Not be Accepted as Established Pursuant to Rule 91(f)` - - `Motion for Partial Summary Judgment` - - `Motion for Pretrial Conference` - - `Motion for Protective Order Pursuant to Rule 103` - - `Motion for Reasonable Litigation or Administrative Costs` - - `Motion for Reconsideration of Findings or Opinion Pursuant to Rule 161` - - `Motion for Reconsideration of Order` - - `Motion for Recusal of Judge` - - `Motion for Review of Jeopardy Assessment or Jeopardy Levy Pursuant to Rule 56` - - `Motion for the Court to Pay the Expenses of a Transcript` - - `Motion for the Court to Pay the Expenses of an Interpreter` - - `Motion for Voluntary Binding Arbitration` - - `Motion for Writ of Habeas Corpus Ad Testificandum` - - `Motion in Limine` - - `Motion to Add Lien or Levy Designation` - - `Motion to Add Small Tax case Designation` - - `Motion to Amend Order` - - `Motion to Appoint an Interpreter Pursuant to Rule 143(f)` - - `Motion to Appoint New Tax Matters Partner` - - `Motion to Appoint Tax Matters Partner` - - `Motion to Authorize Proposed Sale of Seized Property` - - `Motion to Be Excused from Appearing at the Trial Session` - - `Motion to Be Recognized as Next Friend` - - `Motion to Bifurcate` - - `Motion to Calendar` - - `Motion to Calendar and Consolidate` - - `Motion to Calendar in the Electronic (North) Courtroom` - - `Motion to Certify for Interlocutory Appeal` - - `Motion to Change or Correct Docket Entry` - - `Motion to Change Place of Submission of Declaratory Judgment Case` - - `Motion to Change Place of Trial` - - `Motion to Change Service Method` - - `Motion to Clarify Order` - - `Motion to Close on Ground of Duplication` - - `Motion to Compel Discovery` - - `Motion to Compel Production of Documents` - - `Motion to Compel Responses to Interrogatories` - - `Motion to Compel the Taking of Deposition` - - `Motion to Conform the Pleadings to the Proof` - - `Motion to Consolidate` - - `Motion to Correct and Certify Record on Appeal` - - `Motion to Correct Clerical Order` - - `Motion to Correct Transcript` - - `Motion to Depose Pursuant to Rule 74` - - `Motion to Determine the Tax Matters Partner` - - `Motion to Dismiss` - - `Motion to Dismiss for Failure to Properly Prosecute` - - `Motion to Dismiss for Failure to State a Claim upon Which Relief Can Be Granted` - - `Motion to Dismiss for Lack of Jurisdiction as to [person, notice, or year]` - - `Motion to Dismiss on Grounds of Mootness` - - `Motion to Disqualify Counsel` - - `Motion to Enforce a Refund of Overpayment Pursuant to Rule 260` - - `Motion to Enforce Subpoena` - - `Motion to Extend Time to Move or File Answer` - - `Motion to Impose a Penalty` - - `Motion to Impose Sanctions` - - `Motion to Modify Decision in Estate Tax Case Pursuant to Rule 262` - - `Motion to Modify Order` - - `Motion to Permit Expert Witness to Testify without a Written Report Regarding Industry Practice Pursuant to Rule 143(g)(3)` - - `Motion to Permit Levy` - - `Motion to Preclude` - - `Motion to Quash or Modify Subpoena` - - `Motion to Redetermine Interest Pursuant to Rule 261` - - `Motion to Remand` - - `Motion to Remove Lien/Levy Designation` - - `Motion to Remove Small Tax Case Designation` - - `Motion to Remove Tax Matters Partner` - - `Motion to Reopen the Record` - - `Motion to Require Petitioner to File a Reply in a Small Tax Case Pursuant to Rule 173(c)` - - `Motion to Restore Case to the General Docket` - - `Motion to Restrain Assessment or Collection or to Order Refund of Amount Collected` - - `Motion to Retain File in Estate Tax Case Involving § 6166 Election Pursuant to Rule 157` - - `Motion to Review the Sufficiency of Answers or Objections to Request for Admissions` - - `Motion to Seal` - - `Motion to Set for a Time & Date Certain` - - `Motion to Set Pretrial Scheduling Order` - - `Motion to Sever` - - `Motion to Shift the Burden of Proof` - - `Motion to Shorten the Time` - - `Motion to Stay Proceedings` - - `Motion to Stay Proposed Sale of Seized Property` - - `Motion to Strike` - - `Motion to Submit Case Pursuant to Rule 122` - - `Motion to Substitute Parties and Change Caption` - - `Motion to Substitute Trial Exhibit(s)` - - `Motion to Supplement the Record` - - `Motion to Suppress Evidence` - - `Motion to Take Deposition Pursuant to Rule 74(c)(3)` - - `Motion to Take Judicial Notice` - - `Motion to Vacate` - - `Motion to Vacate or Revise Pursuant to Rule 162` - - `Motion to Withdraw` - - `Motion to Withdraw or Modify the Deemed Admitted Admissions Pursuant to Rule 90(f)` - - `Notice of Abatement of Jeopardy Assessment` - - `Notice of Appeal` - - `Notice of Change of Address` - - `Notice of Change of Address and Telephone Number` - - `Notice of Change of Telephone Number` - - `Notice of Clarification of Tax Matters Partner` - - `Notice of Concession` - - `Notice of Consistent Agreement Pursuant to Rule 248(c)(1)` - - `Notice of Death of Counsel` - - `Notice of Filing of Petition and Right to Intervene` - - `Notice of Filing of the Administrative Record` - - `Notice of Identification of Tax Matters Partner` - - `Notice of Intent Not to File` - - `Notice of Issue Concerning Foreign Law` - - `Notice of Jeopardy Assessment` - - `Notice of Judicial Ruling` - - `Notice of No Objection` - - `Notice of Objection` - - `Notice of Partial Abatement of Jeopardy Assessment` - - `Notice of Proceeding in Bankruptcy` - - `Notice of Relevant Judicial Decisions` - - `Notice of Settlement Agreement Pursuant to Rule 248(c)(1)` - - `Notice of Small Tax Case Election` - - `Notice of Supplemental Authority` - - `Notice of Telephone Number` - - `Notice of Termination Assessment` - - `Notice of Unavailability` - - `Redacted Petition Filed` - - `Prehearing Memorandum` - - `Pretrial Memorandum` - - `Reply` - - `Sur-Reply` - - `Request for Admissions` - - `Request for Judicial Notice` - - `Request for Pretrial Conference` - - `No Objection` - - `Objection` - - `Opposition` - - `Response` - - `Seriatim Answering Brief` - - `Seriatim Answering Memorandum Brief` - - `Seriatim Opening Brief` - - `Seriatim Opening Memorandum Brief` - - `Seriatim Reply Brief` - - `Seriatim Reply Memorandum Brief` - - `Seriatim Sur-Reply Brief` - - `Seriatim Sur-Reply Memorandum Brief` - - `Simultaneous Answering Brief` - - `Simultaneous Answering Memoranda of Law` - - `Simultaneous Answering Memorandum Brief` - - `Simultaneous Memoranda of Law` - - `Simultaneous Opening Brief` - - `Simultaneous Opening Memorandum Brief` - - `Simultaneous Reply Brief` - - `Simultaneous Supplemental Brief` - - `Simultaneous Sur-Reply Brief` - - `Simultaneous Sur-Reply Memorandum Brief` - - `Statement` - - `Statement of Redacted Information` - - `Statement under Rule 212` - - `Statement under Rule 50(c)` - - `Settlement Stipulation` - - `Stipulation` - - `Stipulation as to the Administrative Record` - - `Stipulation as to the Partial Administrative Record` - - `Stipulation of Facts` - - `Stipulation of Pretrial Deadlines` - - `Stipulation of Settled Issues` - - `Stipulation of Settlement` - - `Stipulation to Be Bound` - - `Stipulation to Take Deposition` - - `Supplement` - - `Supplemental` - - `Affidavit in Support` - - `Brief in Support` - - `Declaration in Support` - - `Memorandum in Support` - - `Unsworn Declaration under Penalty of Perjury in Support` - - `Application` - - `Application for Examination Pursuant to Rule 73` - - `Amended [Document Name]` - - `Appellate Filing Fee Received` - - `Bond` - - `Bounced Electronic Service` - - `Evidence` - - `Hearing Exhibits` - - `Letter` - - `Miscellaneous` - - `Miscellaneous (Lodged)` - - `Reference List of Redacted Information` - - `Returned Mail` - - `Trial Exhibits` - - `U.S.C.A. [Anything]` - - `Motion` - - `Motion for Review By the Full Court` - - `Motion for Review En Banc` - - `Motion to Be Exempt from E-Filing` - - `Motion to Change Place of Hearing of Disclosure Case` - - `Motion to File Document Under Seal` - - `Motion to Intervene` - - `Motion to Proceed Anonymously` - - `Notice` - - `Notice of Change of Counsel for Non-Party` - - `Notice of Election to Intervene` - - `Notice of Election to Participate` - - `Notice of Intervention` - - `Ratification of Petition` - - `Request` - - `Objection [anything]` - - `Opposition [anything]` - - `Response [anything]` - - `Supplement To [anything]` - - `Supplemental [anything]` - - `Order` - - `Order of Dismissal for Lack of Jurisdiction` - - `Order of Dismissal` - - `Order of Dismissal and Decision` - - `Order to Show Cause` - - `Order and Decision` - - `Decision` - - `O - Order` - - `OAJ - Order that case is assigned` - - `OAL - Order that the letter "L" is added to Docket number` - - `OAP - Order for Amended Petition` - - `OAPF - Order for Amended Petition and Filing Fee` - - `OAR - Order that the letter "R" is added to the Docket number` - - `OAS - Order that the letter "S" is added to the Docket number` - - `OASL - Order that the letters "SL" are added to the Docket number` - - `OAW - Order that the letter "W" is added to the Docket number` - - `OAX - Order that the letter "X" is added to the Docket number` - - `OCA - Order that caption of case is amended` - - `OD - Order of Dismissal Entered,` - - `ODD - Order of Dismissal and Decision Entered,` - - `ODL - Order that the letter "L" is deleted from the Docket number` - - `ODP - Order that the letter "P" is deleted from the Docket number` - - `ODR - Order that the letter "R" is deleted from the Docket number` - - `ODS - Order that the letter "S" is deleted from the Docket number` - - `ODSL - Order that the letters "SL" are deleted from the Docket number` - - `ODW - Order that the letter "W" is deleted from the Docket number` - - `ODX - Order that the letter "X" is deleted from the Docket number` - - `OF - Order for Filing Fee` - - `OFAB - Order fixing amount of bond` - - `OFFX - Order time is extended for petr(s) to pay the filing fee` - - `OFWD - Order for Filing Fee. Application waiver of Filing Fee is denied.` - - `OFX - Order time is extended for petr(s) to pay filing fee or submit an Application for Waiver of Filing fee` - - `OIP - Order that the letter "P" is added to the Docket number` - - `OJR - Order that jurisdiction is retained` - - `OODS - Order for Ownership Disclosure Statement` - - `OPFX - Order time is extended for petr(s) to file Amended Petition and pay the Filing Fee or submit an Application for Waiver of Filing Fee` - - `OPX - Order time is extended for petr(s) to file Amended Petition` - - `ORAP - Order for Amendment to Petition` - - `OROP - Order for Ratification of Petition` - - `OSC - Order` - - `OSCP - Order petr(s) to show cause why "S" should not be removed` - - `OST - Order of Service of Transcript (Bench Opinion)` - - `OSUB - Order that case is submitted` - - `DEC - Decision Entered,` - - `OAD - Order and Decision Entered,` - - `ODJ - Order of Dismissal for Lack of Jurisdiction Entered,` - - `SDEC - Stipulated Decision Entered,` - - `MOP - Memorandum Opinion` - - `NOT - Notice` - - `Summary Opinion` - - `Writ of Habeas Corpus Ad Testificandum` - - `CTRA - Corrected Transcript` - - `FTRL - Further Trial before ...` - - `HEAR - Hearing before ...` - - `NTD - Notice of Trial` - - `PTRL - Partial Trial before ...` - - `TRL - Trial before ...` - - `ROA - Record on Appeal` - - `TCOP - T.C. Opinion` - - `RTRA - Revised Transcript` - - `TRAN - Transcript` - - `SPTO - Standing Pre-Trial Order` - - `MISC - Miscellaneous` - - `Stipulated Decision` - - `Notice of Docket Change` - - `Notice of Trial` - - `Standing Pretrial Notice` - - `Standing Pretrial Order` - -### draftState - -> `object` | optional - -##### Can be null. - -### entityName - -> `string` | required - -##### Can be Document. - -### eventCode - -> `string` | optional - -### filedBy - -> `string` | optional - -##### Can be . - -### filingDate - - -Date that this Document was filed. - -> `date` | required - -##### Maximum date - - -`now` - -### freeText - -> `string` | optional - -### freeText2 - -> `string` | optional - -### hasSupportingDocuments - -> `boolean` | optional - -### isFileAttached - -> `boolean` | optional - -### isPaper - -> `boolean` | optional - -### judge - - -The judge associated with the document. - -> `string` | optional - -##### Can be null. - -### lodged - - -A lodged document is awaiting action by the judge to enact or refuse. - -> `boolean` | optional - -### numberOfPages - -> `number` | optional - -##### Can be null. - -### objections - -> `string` | optional - -### ordinalValue - -> `string` | optional - -### partyIrsPractitioner - -> `boolean` | optional - -### partyPrimary - - -Use the primary contact to compose the filedBy text. - -> `boolean` | optional - -### partySecondary - - -Use the secondary contact to compose the filedBy text. - -> `boolean` | optional - -### pending - -> `boolean` | optional - -### previousDocument - -> `object` | optional - -### privatePractitioners - - -Practitioner names to be used to compose the filedBy text. - -> `array` | optional - - -An array of objects. - -### processingStatus - -> `string` | optional - -### qcAt - -> `date` | optional - -### qcByUserId - -> `string` | optional - -##### Can be null. - -### receivedAt - -> `date` | optional - -### relationship - -> `string` | optional - -##### Allowed Values - - - - `primaryDocument` - - `primarySupportingDocument` - - `secondaryDocument` - - `secondarySupportingDocument` - - `supportingDocument` - -### scenario - -> `string` | optional - -##### Allowed Values - - - - `Standard` - - `Nonstandard A` - - `Nonstandard B` - - `Nonstandard C` - - `Nonstandard D` - - `Nonstandard E` - - `Nonstandard F` - - `Nonstandard G` - - `Nonstandard H` - - `Type A` - - `Type B` - - `Type C` - - `Type D` - - `Type E` - - `Type F` - - `Type G` - - `Type H` - -### secondaryDate - - -A secondary date associated with the document, typically related to time-restricted availability. - -> `date` | optional - -### servedAt - - -When the document is served on the parties. - -> `date` | optional - -### servedParties - -> `array` | optional - - -An array of objects. - -### serviceDate - - -Certificate of service date. - -> `date` | optional - -##### Maximum date - - -`now` - -##### Can be null. - -### serviceStamp - -> `string` | optional - -### signedAt - -> `date` | optional - -##### Can be null. - -### signedByUserId - -> `string` | optional - -##### Can be null. - -### signedJudgeName - -> `string` | optional - -##### Can be null. - -### supportingDocument - -> `string` | optional - -##### Can be null. - -### trialLocation - - -An optional trial location used when generating a fully concatenated document title. - -> `string` | optional - -##### Can be null. - -### userId - -> `string` | required - -### workItems - -> `array` | optional + ``` +--- + type: "object" + keys: + addToCoversheet: + type: "boolean" + flags: + presence: "optional" + additionalInfo: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + additionalInfo2: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + archived: + type: "boolean" + flags: + presence: "optional" + description: "A document that was archived instead of added to the Docket Record." + certificateOfService: + type: "boolean" + flags: + presence: "optional" + certificateOfServiceDate: + type: "any" + whens: + - + ref: + path: + - "certificateOfService" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - true + then: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + otherwise: + type: "any" + flags: + presence: "optional" + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "When the Document was added to the system." + date: + type: "date" + flags: + format: "iso" + presence: "optional" + description: "An optional date used when generating a fully concatenated document title." + allow: + - null + docketNumber: + type: "string" + flags: + presence: "optional" + description: "Docket Number of the associated Case in XXXXX-YY format." + rules: + - + name: "pattern" + args: + regex: "/^([1-9]\\d{2,4}-\\d{2})$/" + docketNumbers: + type: "string" + flags: + presence: "optional" + description: "Optional Docket Number text used when generating a fully concatenated document title." + rules: + - + name: "max" + args: + limit: 500 + documentContentsId: + type: "string" + flags: + presence: "optional" + description: "The S3 ID containing the text contents of the document." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + documentId: + type: "string" + flags: + presence: "required" + description: "ID of the associated PDF document in the S3 bucket." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + documentTitle: + type: "string" + flags: + presence: "optional" + description: "The title of this document." + rules: + - + name: "max" + args: + limit: 500 + documentType: + type: "string" + flags: + only: true + presence: "required" + description: "The type of this document." + allow: + - "Application for Waiver of Filing Fee" + - "Ownership Disclosure Statement" + - "Petition" + - "Request for Place of Trial" + - "Statement of Taxpayer Identification" + - "Entry of Appearance" + - "Substitution of Counsel" + - "Answer" + - "Answer to Amended Petition" + - "Answer to Amended Petition, as Amended" + - "Answer to Amendment to Amended Petition" + - "Answer to Amendment to Petition" + - "Answer to Petition, as Amended" + - "Answer to Second Amended Petition" + - "Answer to Second Amendment to Petition" + - "Answer to Supplement to Petition" + - "Answer to Third Amended Petition" + - "Answer to Third Amendment to Petition" + - "Designation of Counsel to Receive Service" + - "Motion to Withdraw as Counsel" + - "Motion to Withdraw Counsel (filed by petitioner)" + - "Application for Waiver of Filing Fee and Affidavit" + - "Application to Take Deposition" + - "Agreed Computation for Entry of Decision" + - "Computation for Entry of Decision" + - "Proposed Stipulated Decision" + - "Revised Computation" + - "Administrative Record" + - "Amended" + - "Amended Certificate of Service" + - "Amendment [anything]" + - "Certificate as to the Genuineness of the Administrative Record" + - "Certificate of Service" + - "Civil Penalty Approval Form" + - "Exhibit(s)" + - "Memorandum" + - "Partial Administrative Record" + - "Ratification" + - "Redacted" + - "Report" + - "Status Report" + - "Motion for Continuance" + - "Motion for Extension of Time" + - "Motion to Dismiss for Lack of Jurisdiction" + - "Motion to Dismiss for Lack of Prosecution" + - "Motion for Summary Judgment" + - "Motion to Change or Correct Caption" + - "Motion for a New Trial" + - "Motion for an Order under Federal Rule of Evidence 502(d)" + - "Motion for an Order under Model Rule of Professional Conduct 4.2" + - "Motion for Appointment of Mediator" + - "Motion for Assignment of Judge" + - "Motion for Audio of Trial Proceeding(s)" + - "Motion for Certification of an Interlocutory Order to Permit Immediate Appeal" + - "Motion for Default and Dismissal" + - "Motion for Entry of Decision" + - "Motion for Entry of Order that Undenied Allegations be Deemed Admitted Pursuant to Rule 37(c)" + - "Motion for Estate Tax Deduction Developing at or after Trial Pursuant to Rule 156" + - "Motion for in Camera Review" + - "Motion for International Judicial Assistance" + - "Motion for Judgment on the Pleadings" + - "Motion for Leave to Conduct Discovery Pursuant to Rule 70(a)(2)" + - "Motion for Leave to File" + - "Motion for Leave to File Out of Time" + - "Motion for Leave to Serve Additional Interrogatories" + - "Motion for Leave to Use Electronic Equipment" + - "Motion for More Definite Statement Pursuant to Rule 51" + - "Motion for Non-Binding Mediation" + - "Motion for Oral Argument" + - "Motion for Order Fixing Amount of an Appeal Bond" + - "Motion for Order to Release the Amount of an Appeal Bond" + - "Motion for Order to Show Cause Why Case Should Not Be Sumitted on the Basis of the Administrative Record" + - "Motion for Order to Show Cause Why Judgment Should Not be Entered on the Basis of a Previously Decided Case" + - "Motion for Order to Show Cause Why Proposed Facts and Evidence Should Not be Accepted as Established Pursuant to Rule 91(f)" + - "Motion for Partial Summary Judgment" + - "Motion for Pretrial Conference" + - "Motion for Protective Order Pursuant to Rule 103" + - "Motion for Reasonable Litigation or Administrative Costs" + - "Motion for Reconsideration of Findings or Opinion Pursuant to Rule 161" + - "Motion for Reconsideration of Order" + - "Motion for Recusal of Judge" + - "Motion for Review of Jeopardy Assessment or Jeopardy Levy Pursuant to Rule 56" + - "Motion for the Court to Pay the Expenses of a Transcript" + - "Motion for the Court to Pay the Expenses of an Interpreter" + - "Motion for Voluntary Binding Arbitration" + - "Motion for Writ of Habeas Corpus Ad Testificandum" + - "Motion in Limine" + - "Motion to Add Lien or Levy Designation" + - "Motion to Add Small Tax case Designation" + - "Motion to Amend Order" + - "Motion to Appoint an Interpreter Pursuant to Rule 143(f)" + - "Motion to Appoint New Tax Matters Partner" + - "Motion to Appoint Tax Matters Partner" + - "Motion to Authorize Proposed Sale of Seized Property" + - "Motion to Be Excused from Appearing at the Trial Session" + - "Motion to Be Recognized as Next Friend" + - "Motion to Bifurcate" + - "Motion to Calendar" + - "Motion to Calendar and Consolidate" + - "Motion to Calendar in the Electronic (North) Courtroom" + - "Motion to Certify for Interlocutory Appeal" + - "Motion to Change or Correct Docket Entry" + - "Motion to Change Place of Submission of Declaratory Judgment Case" + - "Motion to Change Place of Trial" + - "Motion to Change Service Method" + - "Motion to Clarify Order" + - "Motion to Close on Ground of Duplication" + - "Motion to Compel Discovery" + - "Motion to Compel Production of Documents" + - "Motion to Compel Responses to Interrogatories" + - "Motion to Compel the Taking of Deposition" + - "Motion to Conform the Pleadings to the Proof" + - "Motion to Consolidate" + - "Motion to Correct and Certify Record on Appeal" + - "Motion to Correct Clerical Order" + - "Motion to Correct Transcript" + - "Motion to Depose Pursuant to Rule 74" + - "Motion to Determine the Tax Matters Partner" + - "Motion to Dismiss" + - "Motion to Dismiss for Failure to Properly Prosecute" + - "Motion to Dismiss for Failure to State a Claim upon Which Relief Can Be Granted" + - "Motion to Dismiss for Lack of Jurisdiction as to [person, notice, or year]" + - "Motion to Dismiss on Grounds of Mootness" + - "Motion to Disqualify Counsel" + - "Motion to Enforce a Refund of Overpayment Pursuant to Rule 260" + - "Motion to Enforce Subpoena" + - "Motion to Extend Time to Move or File Answer" + - "Motion to Impose a Penalty" + - "Motion to Impose Sanctions" + - "Motion to Modify Decision in Estate Tax Case Pursuant to Rule 262" + - "Motion to Modify Order" + - "Motion to Permit Expert Witness to Testify without a Written Report Regarding Industry Practice Pursuant to Rule 143(g)(3)" + - "Motion to Permit Levy" + - "Motion to Preclude" + - "Motion to Quash or Modify Subpoena" + - "Motion to Redetermine Interest Pursuant to Rule 261" + - "Motion to Remand" + - "Motion to Remove Lien/Levy Designation" + - "Motion to Remove Small Tax Case Designation" + - "Motion to Remove Tax Matters Partner" + - "Motion to Reopen the Record" + - "Motion to Require Petitioner to File a Reply in a Small Tax Case Pursuant to Rule 173(c)" + - "Motion to Restore Case to the General Docket" + - "Motion to Restrain Assessment or Collection or to Order Refund of Amount Collected" + - "Motion to Retain File in Estate Tax Case Involving § 6166 Election Pursuant to Rule 157" + - "Motion to Review the Sufficiency of Answers or Objections to Request for Admissions" + - "Motion to Seal" + - "Motion to Set for a Time & Date Certain" + - "Motion to Set Pretrial Scheduling Order" + - "Motion to Sever" + - "Motion to Shift the Burden of Proof" + - "Motion to Shorten the Time" + - "Motion to Stay Proceedings" + - "Motion to Stay Proposed Sale of Seized Property" + - "Motion to Strike" + - "Motion to Submit Case Pursuant to Rule 122" + - "Motion to Substitute Parties and Change Caption" + - "Motion to Substitute Trial Exhibit(s)" + - "Motion to Supplement the Record" + - "Motion to Suppress Evidence" + - "Motion to Take Deposition Pursuant to Rule 74(c)(3)" + - "Motion to Take Judicial Notice" + - "Motion to Vacate" + - "Motion to Vacate or Revise Pursuant to Rule 162" + - "Motion to Withdraw" + - "Motion to Withdraw or Modify the Deemed Admitted Admissions Pursuant to Rule 90(f)" + - "Notice of Abatement of Jeopardy Assessment" + - "Notice of Appeal" + - "Notice of Change of Address" + - "Notice of Change of Address and Telephone Number" + - "Notice of Change of Telephone Number" + - "Notice of Clarification of Tax Matters Partner" + - "Notice of Concession" + - "Notice of Consistent Agreement Pursuant to Rule 248(c)(1)" + - "Notice of Death of Counsel" + - "Notice of Filing of Petition and Right to Intervene" + - "Notice of Filing of the Administrative Record" + - "Notice of Identification of Tax Matters Partner" + - "Notice of Intent Not to File" + - "Notice of Issue Concerning Foreign Law" + - "Notice of Jeopardy Assessment" + - "Notice of Judicial Ruling" + - "Notice of No Objection" + - "Notice of Objection" + - "Notice of Partial Abatement of Jeopardy Assessment" + - "Notice of Proceeding in Bankruptcy" + - "Notice of Relevant Judicial Decisions" + - "Notice of Settlement Agreement Pursuant to Rule 248(c)(1)" + - "Notice of Small Tax Case Election" + - "Notice of Supplemental Authority" + - "Notice of Telephone Number" + - "Notice of Termination Assessment" + - "Notice of Unavailability" + - "Redacted Petition Filed" + - "Prehearing Memorandum" + - "Pretrial Memorandum" + - "Reply" + - "Sur-Reply" + - "Request for Admissions" + - "Request for Judicial Notice" + - "Request for Pretrial Conference" + - "No Objection" + - "Objection" + - "Opposition" + - "Response" + - "Seriatim Answering Brief" + - "Seriatim Answering Memorandum Brief" + - "Seriatim Opening Brief" + - "Seriatim Opening Memorandum Brief" + - "Seriatim Reply Brief" + - "Seriatim Reply Memorandum Brief" + - "Seriatim Sur-Reply Brief" + - "Seriatim Sur-Reply Memorandum Brief" + - "Simultaneous Answering Brief" + - "Simultaneous Answering Memoranda of Law" + - "Simultaneous Answering Memorandum Brief" + - "Simultaneous Memoranda of Law" + - "Simultaneous Opening Brief" + - "Simultaneous Opening Memorandum Brief" + - "Simultaneous Reply Brief" + - "Simultaneous Supplemental Brief" + - "Simultaneous Sur-Reply Brief" + - "Simultaneous Sur-Reply Memorandum Brief" + - "Statement" + - "Statement of Redacted Information" + - "Statement under Rule 212" + - "Statement under Rule 50(c)" + - "Settlement Stipulation" + - "Stipulation" + - "Stipulation as to the Administrative Record" + - "Stipulation as to the Partial Administrative Record" + - "Stipulation of Facts" + - "Stipulation of Pretrial Deadlines" + - "Stipulation of Settled Issues" + - "Stipulation of Settlement" + - "Stipulation to Be Bound" + - "Stipulation to Take Deposition" + - "Supplement" + - "Supplemental" + - "Affidavit in Support" + - "Brief in Support" + - "Declaration in Support" + - "Memorandum in Support" + - "Unsworn Declaration under Penalty of Perjury in Support" + - "Application" + - "Application for Examination Pursuant to Rule 73" + - "Amended [Document Name]" + - "Appellate Filing Fee Received" + - "Bond" + - "Bounced Electronic Service" + - "Evidence" + - "Hearing Exhibits" + - "Letter" + - "Miscellaneous" + - "Miscellaneous (Lodged)" + - "Reference List of Redacted Information" + - "Returned Mail" + - "Trial Exhibits" + - "U.S.C.A. [Anything]" + - "Motion" + - "Motion for Review By the Full Court" + - "Motion for Review En Banc" + - "Motion to Be Exempt from E-Filing" + - "Motion to Change Place of Hearing of Disclosure Case" + - "Motion to File Document Under Seal" + - "Motion to Intervene" + - "Motion to Proceed Anonymously" + - "Notice" + - "Notice of Change of Counsel for Non-Party" + - "Notice of Election to Intervene" + - "Notice of Election to Participate" + - "Notice of Intervention" + - "Ratification of Petition" + - "Request" + - "Objection [anything]" + - "Opposition [anything]" + - "Response [anything]" + - "Supplement To [anything]" + - "Supplemental [anything]" + - "Order" + - "Order of Dismissal for Lack of Jurisdiction" + - "Order of Dismissal" + - "Order of Dismissal and Decision" + - "Order to Show Cause" + - "Order and Decision" + - "Decision" + - "O - Order" + - "OAJ - Order that case is assigned" + - "OAL - Order that the letter \"L\" is added to Docket number" + - "OAP - Order for Amended Petition" + - "OAPF - Order for Amended Petition and Filing Fee" + - "OAR - Order that the letter \"R\" is added to the Docket number" + - "OAS - Order that the letter \"S\" is added to the Docket number" + - "OASL - Order that the letters \"SL\" are added to the Docket number" + - "OAW - Order that the letter \"W\" is added to the Docket number" + - "OAX - Order that the letter \"X\" is added to the Docket number" + - "OCA - Order that caption of case is amended" + - "OD - Order of Dismissal Entered" + - "ODD - Order of Dismissal and Decision Entered" + - "ODL - Order that the letter \"L\" is deleted from the Docket number" + - "ODP - Order that the letter \"P\" is deleted from the Docket number" + - "ODR - Order that the letter \"R\" is deleted from the Docket number" + - "ODS - Order that the letter \"S\" is deleted from the Docket number" + - "ODSL - Order that the letters \"SL\" are deleted from the Docket number" + - "ODW - Order that the letter \"W\" is deleted from the Docket number" + - "ODX - Order that the letter \"X\" is deleted from the Docket number" + - "OF - Order for Filing Fee" + - "OFAB - Order fixing amount of bond" + - "OFFX - Order time is extended for petr(s) to pay the filing fee" + - "OFWD - Order for Filing Fee. Application waiver of Filing Fee is denied." + - "OFX - Order time is extended for petr(s) to pay filing fee or submit an Application for Waiver of Filing fee" + - "OIP - Order that the letter \"P\" is added to the Docket number" + - "OJR - Order that jurisdiction is retained" + - "OODS - Order for Ownership Disclosure Statement" + - "OPFX - Order time is extended for petr(s) to file Amended Petition and pay the Filing Fee or submit an Application for Waiver of Filing Fee" + - "OPX - Order time is extended for petr(s) to file Amended Petition" + - "ORAP - Order for Amendment to Petition" + - "OROP - Order for Ratification of Petition" + - "OSC - Order" + - "OSCP - Order petr(s) to show cause why \"S\" should not be removed" + - "OST - Order of Service of Transcript (Bench Opinion)" + - "OSUB - Order that case is submitted" + - "DEC - Decision Entered," + - "OAD - Order and Decision Entered," + - "ODJ - Order of Dismissal for Lack of Jurisdiction Entered," + - "SDEC - Stipulated Decision Entered," + - "MOP - Memorandum Opinion" + - "NOT - Notice" + - "Summary Opinion" + - "Writ of Habeas Corpus Ad Testificandum" + - "CTRA - Corrected Transcript" + - "FTRL - Further Trial before ..." + - "HEAR - Hearing before ..." + - "NTD - Notice of Trial" + - "PTRL - Partial Trial before ..." + - "TRL - Trial before ..." + - "ROA - Record on Appeal" + - "TCOP - T.C. Opinion" + - "RTRA - Revised Transcript" + - "TRAN - Transcript" + - "SPTO - Standing Pre-Trial Order" + - "MISC - Miscellaneous" + - "Stipulated Decision" + - "Notice of Docket Change" + - "Notice of Trial" + - "Standing Pretrial Notice" + - "Standing Pretrial Order" + draftState: + type: "object" + flags: + presence: "optional" + allow: + - null + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Document" + eventCode: + type: "string" + flags: + presence: "optional" + filedBy: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + allow: + - "" + filingDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "Date that this Document was filed." + rules: + - + name: "max" + args: + date: "now" + freeText: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + freeText2: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + hasSupportingDocuments: + type: "boolean" + flags: + presence: "optional" + isFileAttached: + type: "boolean" + flags: + presence: "optional" + isPaper: + type: "boolean" + flags: + presence: "optional" + judge: + type: "string" + flags: + presence: "optional" + description: "The judge associated with the document." + allow: + - null + lodged: + type: "boolean" + flags: + presence: "optional" + description: "A lodged document is awaiting action by the judge to enact or refuse." + numberOfPages: + type: "number" + flags: + presence: "optional" + allow: + - null + objections: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "No" + - "Yes" + - "Unknown" + ordinalValue: + type: "string" + flags: + presence: "optional" + partyIrsPractitioner: + type: "boolean" + flags: + presence: "optional" + partyPrimary: + type: "boolean" + flags: + presence: "optional" + description: "Use the primary contact to compose the filedBy text." + partySecondary: + type: "boolean" + flags: + presence: "optional" + description: "Use the secondary contact to compose the filedBy text." + pending: + type: "boolean" + flags: + presence: "optional" + previousDocument: + type: "object" + flags: + presence: "optional" + privatePractitioners: + type: "array" + flags: + presence: "optional" + description: "Practitioner names to be used to compose the filedBy text." + items: + - + type: "object" + keys: + name: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 500 + processingStatus: + type: "string" + flags: + presence: "optional" + qcAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + qcByUserId: + type: "string" + flags: + presence: "optional" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + receivedAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + relationship: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "primaryDocument" + - "primarySupportingDocument" + - "secondaryDocument" + - "secondarySupportingDocument" + - "supportingDocument" + scenario: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Standard" + - "Nonstandard A" + - "Nonstandard B" + - "Nonstandard C" + - "Nonstandard D" + - "Nonstandard E" + - "Nonstandard F" + - "Nonstandard G" + - "Nonstandard H" + - "Type A" + - "Type B" + - "Type C" + - "Type D" + - "Type E" + - "Type F" + - "Type G" + - "Type H" + secondaryDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + description: "A secondary date associated with the document, typically related to time-restricted availability." + servedAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + description: "When the document is served on the parties." + servedParties: + type: "array" + flags: + presence: "optional" + items: + - + type: "object" + keys: + name: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 500 + serviceDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + description: "Certificate of service date." + rules: + - + name: "max" + args: + date: "now" + allow: + - null + serviceStamp: + type: "string" + flags: + presence: "optional" + signedAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + allow: + - null + signedByUserId: + type: "string" + flags: + presence: "optional" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + signedJudgeName: + type: "string" + flags: + presence: "optional" + allow: + - null + supportingDocument: + type: "string" + flags: + presence: "optional" + allow: + - null + trialLocation: + type: "string" + flags: + presence: "optional" + description: "An optional trial location used when generating a fully concatenated document title." + allow: + - null + userId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + workItems: + type: "array" + flags: + presence: "optional" + + ``` diff --git a/docs/entities/ForwardMessage.md b/docs/entities/ForwardMessage.md new file mode 100644 index 00000000000..e92aa17e980 --- /dev/null +++ b/docs/entities/ForwardMessage.md @@ -0,0 +1,71 @@ +# ForwardMessage + ``` +--- + type: "object" + keys: + assigneeId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + forwardMessage: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 500 + section: + type: "string" + flags: + only: true + presence: "required" + allow: + - "adc" + - "admissions" + - "chambers" + - "clerkofcourt" + - "docket" + - "petitions" + - "trialClerks" + - "armensChambers" + - "ashfordsChambers" + - "buchsChambers" + - "carluzzosChambers" + - "cohensChambers" + - "colvinsChambers" + - "copelandsChambers" + - "foleysChambers" + - "galesChambers" + - "gerbersChambers" + - "goekesChambers" + - "gustafsonsChambers" + - "guysChambers" + - "halpernsChambers" + - "holmesChambers" + - "jacobsChambers" + - "jonesChambers" + - "kerrigansChambers" + - "laubersChambers" + - "leydensChambers" + - "marvelsChambers" + - "morrisonsChambers" + - "negasChambers" + - "panuthosChambers" + - "parisChambers" + - "pughsChambers" + - "ruwesChambers" + - "thorntonsChambers" + - "urdasChambers" + - "vasquezsChambers" + - "wellsChambers" + + ``` diff --git a/docs/entities/InitialWorkItemMessage.md b/docs/entities/InitialWorkItemMessage.md new file mode 100644 index 00000000000..31deb7a24cb --- /dev/null +++ b/docs/entities/InitialWorkItemMessage.md @@ -0,0 +1,71 @@ +# InitialWorkItemMessage + ``` +--- + type: "object" + keys: + assigneeId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + message: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 500 + section: + type: "string" + flags: + only: true + presence: "required" + allow: + - "adc" + - "admissions" + - "chambers" + - "clerkofcourt" + - "docket" + - "petitions" + - "trialClerks" + - "armensChambers" + - "ashfordsChambers" + - "buchsChambers" + - "carluzzosChambers" + - "cohensChambers" + - "colvinsChambers" + - "copelandsChambers" + - "foleysChambers" + - "galesChambers" + - "gerbersChambers" + - "goekesChambers" + - "gustafsonsChambers" + - "guysChambers" + - "halpernsChambers" + - "holmesChambers" + - "jacobsChambers" + - "jonesChambers" + - "kerrigansChambers" + - "laubersChambers" + - "leydensChambers" + - "marvelsChambers" + - "morrisonsChambers" + - "negasChambers" + - "panuthosChambers" + - "parisChambers" + - "pughsChambers" + - "ruwesChambers" + - "thorntonsChambers" + - "urdasChambers" + - "vasquezsChambers" + - "wellsChambers" + + ``` diff --git a/docs/entities/IrsPractitioner.md b/docs/entities/IrsPractitioner.md new file mode 100644 index 00000000000..ec6cf19287c --- /dev/null +++ b/docs/entities/IrsPractitioner.md @@ -0,0 +1,288 @@ +# IrsPractitioner + ``` +--- + type: "object" + keys: + barNumber: + type: "string" + flags: + presence: "optional" + allow: + - null + contact: + type: "object" + flags: + presence: "optional" + keys: + address1: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + address2: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + address3: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + city: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + - "international" + country: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + otherwise: + type: "string" + flags: + presence: "optional" + allow: + - null + phone: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + postalCode: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + state: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "optional" + allow: + - null + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + email: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "IrsPractitioner" + section: + type: "string" + flags: + presence: "optional" + token: + type: "string" + flags: + presence: "optional" + userId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + name: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + role: + type: "string" + flags: + only: true + presence: "required" + allow: + - "irsPractitioner" + judgeFullName: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + judgeTitle: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + serviceIndicator: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Electronic" + - "None" + - "Paper" + + ``` diff --git a/docs/entities/Message.md b/docs/entities/Message.md new file mode 100644 index 00000000000..e5e169b6d02 --- /dev/null +++ b/docs/entities/Message.md @@ -0,0 +1,85 @@ +# Message + ``` +--- + type: "object" + keys: + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Message" + from: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + fromUserId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + message: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 500 + messageId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + to: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + toUserId: + type: "string" + flags: + presence: "optional" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + + ``` diff --git a/docs/entities/NewCaseMessage.md b/docs/entities/NewCaseMessage.md new file mode 100644 index 00000000000..c50fef4ea11 --- /dev/null +++ b/docs/entities/NewCaseMessage.md @@ -0,0 +1,93 @@ +# NewCaseMessage + ``` +--- + type: "object" + keys: + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "NewCaseMessage" + message: + type: "string" + flags: + presence: "required" + description: "The message text." + rules: + - + name: "max" + args: + limit: 500 + subject: + type: "string" + flags: + presence: "required" + description: "The subject line of the message." + rules: + - + name: "max" + args: + limit: 250 + toSection: + type: "string" + flags: + only: true + presence: "required" + description: "The section of the user who is the recipient of the message." + allow: + - "adc" + - "admissions" + - "chambers" + - "clerkofcourt" + - "docket" + - "petitions" + - "trialClerks" + - "armensChambers" + - "ashfordsChambers" + - "buchsChambers" + - "carluzzosChambers" + - "cohensChambers" + - "colvinsChambers" + - "copelandsChambers" + - "foleysChambers" + - "galesChambers" + - "gerbersChambers" + - "goekesChambers" + - "gustafsonsChambers" + - "guysChambers" + - "halpernsChambers" + - "holmesChambers" + - "jacobsChambers" + - "jonesChambers" + - "kerrigansChambers" + - "laubersChambers" + - "leydensChambers" + - "marvelsChambers" + - "morrisonsChambers" + - "negasChambers" + - "panuthosChambers" + - "parisChambers" + - "pughsChambers" + - "ruwesChambers" + - "thorntonsChambers" + - "urdasChambers" + - "vasquezsChambers" + - "wellsChambers" + toUserId: + type: "string" + flags: + presence: "required" + description: "The ID of the user who is the recipient of the message." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + + ``` diff --git a/docs/entities/NewPractitioner.md b/docs/entities/NewPractitioner.md new file mode 100644 index 00000000000..c11a5684f2f --- /dev/null +++ b/docs/entities/NewPractitioner.md @@ -0,0 +1,398 @@ +# NewPractitioner + ``` +--- + type: "object" + keys: + barNumber: + type: "string" + flags: + presence: "optional" + allow: + - null + contact: + type: "object" + flags: + presence: "optional" + keys: + address1: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + address2: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + address3: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + city: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + - "international" + country: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + otherwise: + type: "string" + flags: + presence: "optional" + allow: + - null + phone: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + postalCode: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + state: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "optional" + allow: + - null + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + email: + type: "string" + flags: + presence: "required" + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Practitioner" + section: + type: "string" + flags: + presence: "optional" + token: + type: "string" + flags: + presence: "optional" + userId: + type: "string" + flags: + presence: "optional" + allow: + - null + name: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + role: + type: "string" + flags: + presence: "optional" + allow: + - null + judgeFullName: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + judgeTitle: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + additionalPhone: + type: "string" + flags: + presence: "optional" + description: "An alternate phone number for the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + admissionsDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "The date the practitioner was admitted to the Tax Court bar." + rules: + - + name: "max" + args: + date: "now" + admissionsStatus: + type: "string" + flags: + presence: "required" + alternateEmail: + type: "string" + flags: + presence: "optional" + description: "An alternate email address for the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + birthYear: + type: "number" + flags: + presence: "required" + description: "The year the practitioner was born." + rules: + - + name: "integer" + - + name: "min" + args: + limit: 1900 + - + name: "max" + args: + limit: 2020 + employer: + type: "string" + flags: + only: true + presence: "required" + description: "The employer designation for the practitioner." + allow: + - "IRS" + - "DOJ" + - "Private" + firmName: + type: "string" + flags: + presence: "optional" + description: "The firm name for the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + firstName: + type: "string" + flags: + presence: "required" + lastName: + type: "string" + flags: + presence: "required" + middleName: + type: "string" + flags: + presence: "optional" + description: "The optional middle name of the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + originalBarState: + type: "string" + flags: + presence: "required" + description: "The state in which the practitioner passed their bar examination." + rules: + - + name: "max" + args: + limit: 100 + practitionerType: + type: "string" + flags: + only: true + presence: "required" + description: "The type of practitioner - either Attorney or Non-Attorney." + allow: + - "Attorney" + - "Non-Attorney" + suffix: + type: "string" + flags: + presence: "optional" + description: "The name suffix of the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - "" + + ``` diff --git a/docs/entities/Note.md b/docs/entities/Note.md new file mode 100644 index 00000000000..0aa5fa058b2 --- /dev/null +++ b/docs/entities/Note.md @@ -0,0 +1,11 @@ +# Note + ``` +--- + type: "object" + keys: + notes: + type: "string" + flags: + presence: "required" + + ``` diff --git a/docs/entities/Order.md b/docs/entities/Order.md new file mode 100644 index 00000000000..a4e5f62d935 --- /dev/null +++ b/docs/entities/Order.md @@ -0,0 +1,33 @@ +# Order + ``` +--- + type: "object" + keys: + documentTitle: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + documentType: + type: "string" + flags: + presence: "required" + eventCode: + type: "string" + flags: + presence: "optional" + orderBody: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 500 + + ``` diff --git a/docs/entities/OrderWithoutBody.md b/docs/entities/OrderWithoutBody.md new file mode 100644 index 00000000000..603ebcb8622 --- /dev/null +++ b/docs/entities/OrderWithoutBody.md @@ -0,0 +1,19 @@ +# OrderWithoutBody + ``` +--- + type: "object" + keys: + documentTitle: + type: "string" + flags: + presence: "required" + documentType: + type: "string" + flags: + presence: "required" + eventCode: + type: "string" + flags: + presence: "required" + + ``` diff --git a/docs/entities/Practitioner.md b/docs/entities/Practitioner.md new file mode 100644 index 00000000000..f777ee6bf1b --- /dev/null +++ b/docs/entities/Practitioner.md @@ -0,0 +1,455 @@ +# Practitioner + ``` +--- + type: "object" + keys: + barNumber: + type: "string" + flags: + presence: "required" + description: "A unique identifier comprising of the practitioner initials, date, and series number." + rules: + - + name: "max" + args: + limit: 100 + contact: + type: "object" + flags: + presence: "optional" + keys: + address1: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + address2: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + address3: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + city: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + - "international" + country: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + otherwise: + type: "string" + flags: + presence: "optional" + allow: + - null + phone: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + postalCode: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + state: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "optional" + allow: + - null + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + email: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Practitioner" + section: + type: "string" + flags: + presence: "optional" + token: + type: "string" + flags: + presence: "optional" + userId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + name: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + role: + type: "alternatives" + matches: + - + ref: + path: + - "admissionsStatus" + is: + type: "any" + flags: + only: true + allow: + - "Active" + then: + type: "string" + flags: + only: true + presence: "required" + allow: + - "irsPractitioner" + - "privatePractitioner" + otherwise: + type: "string" + flags: + only: true + presence: "required" + allow: + - "inactivePractitioner" + judgeFullName: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + judgeTitle: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + additionalPhone: + type: "string" + flags: + presence: "optional" + description: "An alternate phone number for the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + admissionsDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + description: "The date the practitioner was admitted to the Tax Court bar." + rules: + - + name: "max" + args: + date: "now" + admissionsStatus: + type: "string" + flags: + only: true + presence: "required" + description: "The Tax Court bar admission status for the practitioner." + allow: + - "Active" + - "Suspended" + - "Disbarred" + - "Resigned" + - "Deceased" + - "Inactive" + alternateEmail: + type: "string" + flags: + presence: "optional" + description: "An alternate email address for the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + birthYear: + type: "number" + flags: + presence: "required" + description: "The year the practitioner was born." + rules: + - + name: "integer" + - + name: "min" + args: + limit: 1900 + - + name: "max" + args: + limit: 2020 + employer: + type: "string" + flags: + only: true + presence: "required" + description: "The employer designation for the practitioner." + allow: + - "IRS" + - "DOJ" + - "Private" + firmName: + type: "string" + flags: + presence: "optional" + description: "The firm name for the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + firstName: + type: "string" + flags: + presence: "required" + description: "The first name of the practitioner." + rules: + - + name: "max" + args: + limit: 100 + lastName: + type: "string" + flags: + presence: "required" + description: "The last name of the practitioner." + rules: + - + name: "max" + args: + limit: 100 + middleName: + type: "string" + flags: + presence: "optional" + description: "The optional middle name of the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + originalBarState: + type: "string" + flags: + presence: "required" + description: "The state in which the practitioner passed their bar examination." + rules: + - + name: "max" + args: + limit: 100 + practitionerType: + type: "string" + flags: + only: true + presence: "required" + description: "The type of practitioner - either Attorney or Non-Attorney." + allow: + - "Attorney" + - "Non-Attorney" + suffix: + type: "string" + flags: + presence: "optional" + description: "The name suffix of the practitioner." + rules: + - + name: "max" + args: + limit: 100 + allow: + - "" + + ``` diff --git a/docs/entities/PrivatePractitioner.md b/docs/entities/PrivatePractitioner.md new file mode 100644 index 00000000000..4a7fa446fbc --- /dev/null +++ b/docs/entities/PrivatePractitioner.md @@ -0,0 +1,296 @@ +# PrivatePractitioner + ``` +--- + type: "object" + keys: + barNumber: + type: "string" + flags: + presence: "optional" + allow: + - null + contact: + type: "object" + flags: + presence: "optional" + keys: + address1: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + address2: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + address3: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + city: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + - "international" + country: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + otherwise: + type: "string" + flags: + presence: "optional" + allow: + - null + phone: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + postalCode: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + state: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "optional" + allow: + - null + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + email: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "PrivatePractitioner" + section: + type: "string" + flags: + presence: "optional" + token: + type: "string" + flags: + presence: "optional" + userId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + name: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + role: + type: "string" + flags: + presence: "required" + only: true + allow: + - "privatePractitioner" + judgeFullName: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + judgeTitle: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + representingPrimary: + type: "boolean" + flags: + presence: "optional" + representingSecondary: + type: "boolean" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Electronic" + - "None" + - "Paper" + + ``` diff --git a/docs/entities/PublicUser.md b/docs/entities/PublicUser.md new file mode 100644 index 00000000000..75616048dcf --- /dev/null +++ b/docs/entities/PublicUser.md @@ -0,0 +1,99 @@ +# PublicUser + ``` +--- + type: "object" + keys: + name: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + role: + type: "string" + flags: + only: true + presence: "required" + allow: + - "adc" + - "admin" + - "admissionsclerk" + - "chambers" + - "clerkofcourt" + - "docketclerk" + - "floater" + - "inactivePractitioner" + - "irsPractitioner" + - "irsSuperuser" + - "judge" + - "petitioner" + - "petitionsclerk" + - "privatePractitioner" + - "trialclerk" + judgeFullName: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + judgeTitle: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + + ``` diff --git a/docs/entities/Scan.md b/docs/entities/Scan.md new file mode 100644 index 00000000000..93c44d0c231 --- /dev/null +++ b/docs/entities/Scan.md @@ -0,0 +1,34 @@ +# Scan + ``` +--- + type: "object" + keys: + batches: + type: "array" + flags: + presence: "required" + rules: + - + name: "min" + args: + limit: 1 + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + scanId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + + ``` diff --git a/docs/entities/Statistic.md b/docs/entities/Statistic.md index cd69127b663..220a0037686 100644 --- a/docs/entities/Statistic.md +++ b/docs/entities/Statistic.md @@ -1,71 +1,170 @@ # Statistic - -### deficiencyAmount - - -The amount of the deficiency. - -> `number` | optional - -##### Can be null. - -### entityName - -> `string` | required - -##### Can be Statistic. - -### lastDateOfPeriod - - -Last date of the statistics period. - -> `date` | optional - -##### Maximum date - - -`now` - -##### Can be null. - -### totalPenalties - - -The total amount of penalties for the period or year. - -> `number` | optional - -##### Can be null. - -### year - - -The year of the statistics period. - -> `number` | optional - -##### Minimum limit - - -`1900` - -##### Maximum limit - - -`2020` - -##### Can be null. - -### yearOrPeriod - - -Whether the statistics are for a year or period. - -> `string` | required - -##### Allowed Values - - - - `Year` - - `Period` + ``` +--- + type: "object" + keys: + determinationDeficiencyAmount: + type: "alternatives" + flags: + description: "The amount of the deficiency determined by the Court." + matches: + - + ref: + path: + - "determinationTotalPenalties" + is: + type: "any" + flags: + presence: "required" + invalid: + - null + then: + type: "number" + flags: + presence: "required" + otherwise: + type: "number" + flags: + presence: "optional" + allow: + - null + determinationTotalPenalties: + type: "alternatives" + flags: + description: "The total amount of penalties for the period or year determined by the Court." + matches: + - + ref: + path: + - "determinationDeficiencyAmount" + is: + type: "any" + flags: + presence: "required" + invalid: + - null + then: + type: "number" + flags: + presence: "required" + otherwise: + type: "number" + flags: + presence: "optional" + allow: + - null + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "Statistic" + irsDeficiencyAmount: + type: "number" + flags: + presence: "required" + description: "The amount of the deficiency on the IRS notice." + irsTotalPenalties: + type: "number" + flags: + presence: "required" + description: "The total amount of penalties for the period or year on the IRS notice." + statisticId: + type: "string" + flags: + presence: "required" + description: "Unique statistic ID only used by the system." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + yearOrPeriod: + type: "string" + flags: + presence: "required" + only: true + description: "Whether the statistics are for a year or period." + allow: + - "Year" + - "Period" + lastDateOfPeriod: + type: "any" + flags: + description: "Last date of the statistics period." + whens: + - + ref: + path: + - "yearOrPeriod" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "Period" + then: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + rules: + - + name: "max" + args: + date: "now" + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + year: + type: "any" + flags: + description: "The year of the statistics period." + whens: + - + ref: + path: + - "yearOrPeriod" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "Year" + then: + type: "number" + flags: + presence: "required" + rules: + - + name: "integer" + - + name: "min" + args: + limit: 1900 + - + name: "max" + args: + limit: 2020 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + + ``` diff --git a/docs/entities/User.md b/docs/entities/User.md new file mode 100644 index 00000000000..ae9319383af --- /dev/null +++ b/docs/entities/User.md @@ -0,0 +1,293 @@ +# User + ``` +--- + type: "object" + keys: + barNumber: + type: "string" + flags: + presence: "optional" + allow: + - null + contact: + type: "object" + flags: + presence: "optional" + keys: + address1: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + address2: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + address3: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + city: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + - "international" + country: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + otherwise: + type: "string" + flags: + presence: "optional" + allow: + - null + phone: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + postalCode: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + state: + type: "any" + whens: + - + ref: + path: + - "countryType" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "international" + then: + type: "string" + flags: + presence: "optional" + allow: + - null + otherwise: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + email: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "User" + section: + type: "string" + flags: + presence: "optional" + token: + type: "string" + flags: + presence: "optional" + userId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + name: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + role: + type: "string" + flags: + only: true + presence: "required" + allow: + - "adc" + - "admin" + - "admissionsclerk" + - "chambers" + - "clerkofcourt" + - "docketclerk" + - "floater" + - "inactivePractitioner" + - "irsPractitioner" + - "irsSuperuser" + - "judge" + - "petitioner" + - "petitionsclerk" + - "privatePractitioner" + - "trialclerk" + judgeFullName: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + judgeTitle: + type: "any" + whens: + - + ref: + path: + - "role" + is: + type: "any" + flags: + only: true + presence: "required" + allow: + - + override: true + - "judge" + then: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + otherwise: + type: "any" + flags: + presence: "optional" + allow: + - null + + ``` diff --git a/docs/entities/UserCase.md b/docs/entities/UserCase.md new file mode 100644 index 00000000000..e1e0dbe9990 --- /dev/null +++ b/docs/entities/UserCase.md @@ -0,0 +1,79 @@ +# UserCase + ``` +--- + type: "object" + keys: + caseCaption: + type: "string" + flags: + presence: "required" + description: "The name of the party bringing the case, e.g. \"Carol Williams, Petitioner,\" \"Mark Taylor, Incompetent, Debra Thomas, Next Friend, Petitioner,\" or \"Estate of Test Taxpayer, Deceased, Petitioner.\" This is the first half of the case title." + rules: + - + name: "max" + args: + limit: 500 + caseId: + type: "string" + flags: + presence: "required" + description: "Unique case ID only used by the system." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + docketNumber: + type: "string" + flags: + presence: "required" + description: "Unique case ID in XXXXX-YY format." + rules: + - + name: "pattern" + args: + regex: "/^([1-9]\\d{2,4}-\\d{2})$/" + docketNumberWithSuffix: + type: "string" + flags: + presence: "optional" + description: "Auto-generated from docket number and the suffix." + leadCaseId: + type: "string" + flags: + presence: "optional" + description: "If this case is consolidated, this is the ID of the lead case. It is the lowest docket number in the consolidated group." + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + status: + type: "string" + flags: + only: true + presence: "optional" + description: "Status of the case." + allow: + - "Assigned - Case" + - "Assigned - Motion" + - "Calendared" + - "CAV" + - "Closed" + - "General Docket - Not at Issue" + - "General Docket - At Issue (Ready for Trial)" + - "Jurisdiction Retained" + - "New" + - "On Appeal" + - "Rule 155" + - "Submitted" + metas: + - + tags: + - "Restricted" + + ``` diff --git a/docs/entities/UserCaseNote.md b/docs/entities/UserCaseNote.md new file mode 100644 index 00000000000..e23c719cdb7 --- /dev/null +++ b/docs/entities/UserCaseNote.md @@ -0,0 +1,40 @@ +# UserCaseNote + ``` +--- + type: "object" + keys: + caseId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "UserCaseNote" + notes: + type: "string" + flags: + presence: "required" + userId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + + ``` diff --git a/docs/entities/WorkItem.md b/docs/entities/WorkItem.md new file mode 100644 index 00000000000..c607e5e0342 --- /dev/null +++ b/docs/entities/WorkItem.md @@ -0,0 +1,356 @@ +# WorkItem + ``` +--- + type: "object" + keys: + assigneeId: + type: "string" + flags: + presence: "optional" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + assigneeName: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + associatedJudge: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + caseId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + caseIsInProgress: + type: "boolean" + flags: + presence: "optional" + caseStatus: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Assigned - Case" + - "Assigned - Motion" + - "Calendared" + - "CAV" + - "Closed" + - "General Docket - Not at Issue" + - "General Docket - At Issue (Ready for Trial)" + - "Jurisdiction Retained" + - "New" + - "On Appeal" + - "Rule 155" + - "Submitted" + caseTitle: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 500 + completedAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + completedBy: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + completedByUserId: + type: "string" + flags: + presence: "optional" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + allow: + - null + completedMessage: + type: "string" + flags: + presence: "optional" + rules: + - + name: "max" + args: + limit: 100 + allow: + - null + createdAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + docketNumber: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^([1-9]\\d{2,4}-\\d{2})$/" + docketNumberSuffix: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "W" + - "P" + - "X" + - "R" + - "SL" + - "L" + - "S" + - null + document: + type: "object" + flags: + presence: "required" + entityName: + type: "string" + flags: + only: true + presence: "required" + allow: + - "WorkItem" + hideFromPendingMessages: + type: "boolean" + flags: + presence: "optional" + highPriority: + type: "boolean" + flags: + presence: "optional" + inProgress: + type: "boolean" + flags: + presence: "optional" + isInitializeCase: + type: "boolean" + flags: + presence: "optional" + isQC: + type: "boolean" + flags: + presence: "required" + isRead: + type: "boolean" + flags: + presence: "optional" + messages: + type: "array" + flags: + presence: "required" + items: + - + type: "object" + section: + type: "string" + flags: + only: true + presence: "required" + allow: + - "adc" + - "admissions" + - "chambers" + - "clerkofcourt" + - "docket" + - "petitions" + - "trialClerks" + - "armensChambers" + - "ashfordsChambers" + - "buchsChambers" + - "carluzzosChambers" + - "cohensChambers" + - "colvinsChambers" + - "copelandsChambers" + - "foleysChambers" + - "galesChambers" + - "gerbersChambers" + - "goekesChambers" + - "gustafsonsChambers" + - "guysChambers" + - "halpernsChambers" + - "holmesChambers" + - "jacobsChambers" + - "jonesChambers" + - "kerrigansChambers" + - "laubersChambers" + - "leydensChambers" + - "marvelsChambers" + - "morrisonsChambers" + - "negasChambers" + - "panuthosChambers" + - "parisChambers" + - "pughsChambers" + - "ruwesChambers" + - "thorntonsChambers" + - "urdasChambers" + - "vasquezsChambers" + - "wellsChambers" + - "admin" + - "admissionsclerk" + - "docketclerk" + - "floater" + - "inactivePractitioner" + - "irsPractitioner" + - "irsSuperuser" + - "judge" + - "petitioner" + - "petitionsclerk" + - "privatePractitioner" + - "trialclerk" + - "irsSystem" + sentBy: + type: "string" + flags: + presence: "required" + rules: + - + name: "max" + args: + limit: 100 + sentBySection: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "adc" + - "admissions" + - "chambers" + - "clerkofcourt" + - "docket" + - "petitions" + - "trialClerks" + - "armensChambers" + - "ashfordsChambers" + - "buchsChambers" + - "carluzzosChambers" + - "cohensChambers" + - "colvinsChambers" + - "copelandsChambers" + - "foleysChambers" + - "galesChambers" + - "gerbersChambers" + - "goekesChambers" + - "gustafsonsChambers" + - "guysChambers" + - "halpernsChambers" + - "holmesChambers" + - "jacobsChambers" + - "jonesChambers" + - "kerrigansChambers" + - "laubersChambers" + - "leydensChambers" + - "marvelsChambers" + - "morrisonsChambers" + - "negasChambers" + - "panuthosChambers" + - "parisChambers" + - "pughsChambers" + - "ruwesChambers" + - "thorntonsChambers" + - "urdasChambers" + - "vasquezsChambers" + - "wellsChambers" + - "admin" + - "admissionsclerk" + - "docketclerk" + - "floater" + - "inactivePractitioner" + - "irsPractitioner" + - "irsSuperuser" + - "judge" + - "petitioner" + - "petitionsclerk" + - "privatePractitioner" + - "trialclerk" + sentByUserId: + type: "string" + flags: + presence: "optional" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + trialDate: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "optional" + allow: + - null + updatedAt: + type: "date" + flags: + format: + - "YYYY-MM-DDTHH:mm:ss.SSSZ" + - "YYYY-MM-DD" + presence: "required" + workItemId: + type: "string" + flags: + presence: "required" + rules: + - + name: "guid" + args: + options: + version: + - "uuidv4" + + ``` diff --git a/docs/entities/contacts/NextFriendForIncompetentPersonContact.md b/docs/entities/contacts/NextFriendForIncompetentPersonContact.md index f6f0361cf24..cb363da9ebe 100644 --- a/docs/entities/contacts/NextFriendForIncompetentPersonContact.md +++ b/docs/entities/contacts/NextFriendForIncompetentPersonContact.md @@ -1,71 +1,76 @@ # contacts/NextFriendForIncompetentPersonContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/NextFriendForMinorContact.md b/docs/entities/contacts/NextFriendForMinorContact.md index 3c8415ae598..933ba67469c 100644 --- a/docs/entities/contacts/NextFriendForMinorContact.md +++ b/docs/entities/contacts/NextFriendForMinorContact.md @@ -1,71 +1,76 @@ # contacts/NextFriendForMinorContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PartnershipAsTaxMattersPartnerContact.md b/docs/entities/contacts/PartnershipAsTaxMattersPartnerContact.md index ac4948ce194..727a7e74e63 100644 --- a/docs/entities/contacts/PartnershipAsTaxMattersPartnerContact.md +++ b/docs/entities/contacts/PartnershipAsTaxMattersPartnerContact.md @@ -1,71 +1,76 @@ # contacts/PartnershipAsTaxMattersPartnerContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PartnershipBBAPrimaryContact.md b/docs/entities/contacts/PartnershipBBAPrimaryContact.md index 122ada0a1a2..e567e3cc2cd 100644 --- a/docs/entities/contacts/PartnershipBBAPrimaryContact.md +++ b/docs/entities/contacts/PartnershipBBAPrimaryContact.md @@ -1,71 +1,76 @@ # contacts/PartnershipBBAPrimaryContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PartnershipOtherThanTaxMattersPrimaryContact.md b/docs/entities/contacts/PartnershipOtherThanTaxMattersPrimaryContact.md index 977917fc9b5..3c96d08f998 100644 --- a/docs/entities/contacts/PartnershipOtherThanTaxMattersPrimaryContact.md +++ b/docs/entities/contacts/PartnershipOtherThanTaxMattersPrimaryContact.md @@ -1,71 +1,76 @@ # contacts/PartnershipOtherThanTaxMattersPrimaryContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerConservatorContact.md b/docs/entities/contacts/PetitionerConservatorContact.md index 280e5dcc46f..b8133a1c88d 100644 --- a/docs/entities/contacts/PetitionerConservatorContact.md +++ b/docs/entities/contacts/PetitionerConservatorContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerConservatorContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerCorporationContact.md b/docs/entities/contacts/PetitionerCorporationContact.md index c6db6a5bc85..2dce690692f 100644 --- a/docs/entities/contacts/PetitionerCorporationContact.md +++ b/docs/entities/contacts/PetitionerCorporationContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerCorporationContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | required - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | optional - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "required" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "optional" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerCustodianContact.md b/docs/entities/contacts/PetitionerCustodianContact.md index 6cc189f6e56..0bfb545b462 100644 --- a/docs/entities/contacts/PetitionerCustodianContact.md +++ b/docs/entities/contacts/PetitionerCustodianContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerCustodianContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerDeceasedSpouseContact.md b/docs/entities/contacts/PetitionerDeceasedSpouseContact.md index faacc314b45..fe8e24f895a 100644 --- a/docs/entities/contacts/PetitionerDeceasedSpouseContact.md +++ b/docs/entities/contacts/PetitionerDeceasedSpouseContact.md @@ -1,73 +1,78 @@ # contacts/PetitionerDeceasedSpouseContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | required - -### name - -> `string` | required - -### phone - -> `string` | optional - -##### Can be null. - -### secondaryName - -> `string` | optional - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "required" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + allow: + - null + secondaryName: + type: "string" + flags: + presence: "optional" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerEstateWithExecutorPrimaryContact.md b/docs/entities/contacts/PetitionerEstateWithExecutorPrimaryContact.md index 7fd17e07892..07680942da5 100644 --- a/docs/entities/contacts/PetitionerEstateWithExecutorPrimaryContact.md +++ b/docs/entities/contacts/PetitionerEstateWithExecutorPrimaryContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerEstateWithExecutorPrimaryContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerGuardianContact.md b/docs/entities/contacts/PetitionerGuardianContact.md index 31db1baf901..6d7832667d2 100644 --- a/docs/entities/contacts/PetitionerGuardianContact.md +++ b/docs/entities/contacts/PetitionerGuardianContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerGuardianContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerIntermediaryContact.md b/docs/entities/contacts/PetitionerIntermediaryContact.md index 1de1caad340..055d2ab3867 100644 --- a/docs/entities/contacts/PetitionerIntermediaryContact.md +++ b/docs/entities/contacts/PetitionerIntermediaryContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerIntermediaryContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | optional - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "optional" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerPrimaryContact.md b/docs/entities/contacts/PetitionerPrimaryContact.md index 3fe1fca3a43..8be9c35cadd 100644 --- a/docs/entities/contacts/PetitionerPrimaryContact.md +++ b/docs/entities/contacts/PetitionerPrimaryContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerPrimaryContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | optional - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "optional" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerSpouseContact.md b/docs/entities/contacts/PetitionerSpouseContact.md index 75c50bab6bb..11ecd496cd9 100644 --- a/docs/entities/contacts/PetitionerSpouseContact.md +++ b/docs/entities/contacts/PetitionerSpouseContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerSpouseContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | optional - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "optional" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/PetitionerTrustContact.md b/docs/entities/contacts/PetitionerTrustContact.md index e519b2f3cf8..fcb401050a9 100644 --- a/docs/entities/contacts/PetitionerTrustContact.md +++ b/docs/entities/contacts/PetitionerTrustContact.md @@ -1,71 +1,76 @@ # contacts/PetitionerTrustContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/entities/contacts/SurvivingSpouseContact.md b/docs/entities/contacts/SurvivingSpouseContact.md index f2a262c09d4..44b07d0b676 100644 --- a/docs/entities/contacts/SurvivingSpouseContact.md +++ b/docs/entities/contacts/SurvivingSpouseContact.md @@ -1,71 +1,76 @@ # contacts/SurvivingSpouseContact - -### countryType - -> `string` | required - -##### Can be domestic. - -### address1 - -> `string` | required - -### address2 - -> `string` | optional - -### address3 - -> `string` | optional - -### city - -> `string` | required - -### email - -> `string` | optional - -### inCareOf - -> `string` | optional - -### name - -> `string` | required - -### phone - -> `string` | optional - -### secondaryName - -> `string` | required - -### title - -> `string` | optional - -### serviceIndicator - -> `string` | optional - -##### Allowed Values - - - - `Electronic` - - `None` - - `Paper` - -### state - -> `string` | required - -### postalCode - -> `string` | required - -##### Regex Pattern - - -`/^(\d{5}|\d{5}-\d{4})$/` + ``` +--- + type: "object" + keys: + countryType: + type: "string" + flags: + only: true + presence: "required" + allow: + - "domestic" + address1: + type: "string" + flags: + presence: "required" + address2: + type: "string" + flags: + presence: "optional" + address3: + type: "string" + flags: + presence: "optional" + city: + type: "string" + flags: + presence: "required" + email: + type: "string" + flags: + presence: "optional" + inCareOf: + type: "string" + flags: + presence: "optional" + name: + type: "string" + flags: + presence: "required" + phone: + type: "string" + flags: + presence: "optional" + secondaryName: + type: "string" + flags: + presence: "required" + title: + type: "string" + flags: + presence: "optional" + serviceIndicator: + type: "string" + flags: + only: true + presence: "optional" + allow: + - "Electronic" + - "None" + - "Paper" + state: + type: "string" + flags: + presence: "required" + postalCode: + type: "string" + flags: + presence: "required" + rules: + - + name: "pattern" + args: + regex: "/^(\\d{5}|\\d{5}-\\d{4})$/" + + ``` diff --git a/docs/zap-report.html b/docs/zap-report.html index ec1516a396b..89d25d55b24 100644 --- a/docs/zap-report.html +++ b/docs/zap-report.html @@ -91,7 +91,7 @@

Summary of Alerts

Low1 - Informational3 + Informational2
@@ -109,9 +109,6 @@

Alerts

ELMAH Information LeakInformational1 - - Timestamp Disclosure - UnixInformational1 -

Alert Detail

@@ -184,7 +181,7 @@

Alert Detail

- Description

A response code of 401 was returned by the server.

This may indicate that the application is failing to handle unexpected input correctly.

Raised by the 'Alert on HTTP Response Code Error' script

+ Description

A response code of 403 was returned by the server.

This may indicate that the application is failing to handle unexpected input correctly.

Raised by the 'Alert on HTTP Response Code Error' script

@@ -193,7 +190,7 @@

Alert Detail

- URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api/swagger.json + URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/1759707314229900462 @@ -201,13 +198,13 @@

Alert Detail

- EvidenceHTTP/1.1 401 + EvidenceHTTP/1.1 403 - URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/ + URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api/swagger.json @@ -215,13 +212,13 @@

Alert Detail

- EvidenceHTTP/1.1 403 + EvidenceHTTP/1.1 401 - URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api/swagger.json/ + URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/ @@ -229,13 +226,13 @@

Alert Detail

- EvidenceHTTP/1.1 401 + EvidenceHTTP/1.1 403 - URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api/7050488461149542025 + URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api/swagger.json/ @@ -243,13 +240,13 @@

Alert Detail

- EvidenceHTTP/1.1 403 + EvidenceHTTP/1.1 401 - URLhttps://efcms-dev.ustc-case-mgmt.flexion.us + URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api/4646883449713411221 @@ -263,7 +260,7 @@

Alert Detail

- URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api + URLhttps://efcms-dev.ustc-case-mgmt.flexion.us @@ -277,7 +274,7 @@

Alert Detail

- URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/4778612618116386192 + URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api @@ -410,72 +407,5 @@

Alert Detail

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Informational (Low)Timestamp Disclosure - Unix
Description

A timestamp was disclosed by the application/web server - Unix

URLhttps://efcms-dev.ustc-case-mgmt.flexion.us/api/swagger.json
MethodGET
Evidence49813550
Instances1
Solution

Manually confirm that the timestamp data is not sensitive, and that the data cannot be aggregated to disclose exploitable patterns.

Other information

49813550, which evaluates to: 1971-07-31 13:05:50

Reference

https://www.owasp.org/index.php/Top_10_2013-A6-Sensitive_Data_Exposure

http://projects.webappsec.org/w/page/13246936/Information%20Leakage

CWE Id200
WASC Id13
Source ID3
diff --git a/env-manager/deleteCustomDomains.js b/env-destroy-utils/deleteCustomDomains.js similarity index 85% rename from env-manager/deleteCustomDomains.js rename to env-destroy-utils/deleteCustomDomains.js index 106c94a60bf..6c2ac07ca96 100644 --- a/env-manager/deleteCustomDomains.js +++ b/env-destroy-utils/deleteCustomDomains.js @@ -1,6 +1,6 @@ const { getApiGateway } = require('./getApiGateway'); const { getCustomDomains } = require('./getCustomDomains'); -const { sleep } = require('./sleep'); +const { sleepForMilliseconds } = require('./sleep'); exports.deleteCustomDomains = async ({ environment }) => { const apiGateway = getApiGateway({ environment }); @@ -15,6 +15,6 @@ exports.deleteCustomDomains = async ({ environment }) => { await apiGateway .deleteDomainName({ DomainName: domain.DomainName }) .promise(); - await sleep(100); + await sleepForMilliseconds(100); } }; diff --git a/env-manager/deleteS3Buckets.js b/env-destroy-utils/deleteS3Buckets.js similarity index 87% rename from env-manager/deleteS3Buckets.js rename to env-destroy-utils/deleteS3Buckets.js index 1f8c8493188..ee0bbd720f6 100644 --- a/env-manager/deleteS3Buckets.js +++ b/env-destroy-utils/deleteS3Buckets.js @@ -10,7 +10,11 @@ exports.deleteS3Buckets = async ({ environment }) => { }); for (const bucket of buckets) { - console.log('Deleting items from S3 Bucket:', bucket.Name); + console.log( + 'Deleting items from S3 Bucket:', + environment.region, + bucket.Name, + ); const objects = await s3 .listObjects({ Bucket: bucket.Name, MaxKeys: 1000 }) @@ -48,9 +52,5 @@ exports.deleteS3Buckets = async ({ environment }) => { }) .promise(); } - - await s3.deleteBucket({ Bucket: bucket.Name }).promise(); - - console.log('Deleted S3 Bucket:', bucket.Name); } }; diff --git a/env-manager/deleteStacks.js b/env-destroy-utils/deleteStacks.js similarity index 72% rename from env-manager/deleteStacks.js rename to env-destroy-utils/deleteStacks.js index 58a6968bcfc..e60fd5ac15d 100644 --- a/env-manager/deleteStacks.js +++ b/env-destroy-utils/deleteStacks.js @@ -1,6 +1,6 @@ const { getCloudFormation } = require('./getCloudFormation'); const { getStacks } = require('./getStacks'); -const { sleep } = require('./sleep'); +const { sleepForMilliseconds } = require('./sleep'); exports.deleteStacks = async ({ environment }) => { const cloudFormation = getCloudFormation({ environment }); @@ -9,22 +9,25 @@ exports.deleteStacks = async ({ environment }) => { environment, }); for (const stack of stacks) { - console.log('Delete CloudFormation Stack:', stack.StackName); + console.log( + 'Delete CloudFormation Stack:', + environment.region, + stack.StackName, + ); await cloudFormation.deleteStack({ StackName: stack.StackName }).promise(); - await sleep(100); } let resourceCount = stacks.length; while (resourceCount > 0) { - await sleep(1000); + await sleepForMilliseconds(1000); const refreshedStacks = await getStacks({ cloudFormation, environment, }); console.log( - 'Waiting for stacks to be deleted:', - Date(), + 'Remaining stacks:', + environment.region, refreshedStacks.length, ); resourceCount = refreshedStacks.length; diff --git a/env-destroy-utils/destroyEnvironment.js b/env-destroy-utils/destroyEnvironment.js new file mode 100644 index 00000000000..2082aa85495 --- /dev/null +++ b/env-destroy-utils/destroyEnvironment.js @@ -0,0 +1,79 @@ +const { deleteCustomDomains } = require('./deleteCustomDomains'); +const { deleteS3Buckets } = require('./deleteS3Buckets'); +const { deleteStacks } = require('./deleteStacks'); +const { exec } = require('child_process'); + +const environmentName = process.argv[2] || 'exp1'; + +const environmentEast = { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + apiVersion: 'latest', + name: environmentName, + region: 'us-east-1', + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, +}; + +const environmentWest = { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + apiVersion: 'latest', + name: environmentName, + region: 'us-west-1', + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, +}; + +const teardownEnvironment = async () => { + await Promise.all([ + deleteCustomDomains({ environment: environmentEast }), + deleteCustomDomains({ environment: environmentWest }), + ]); + + await Promise.all([ + deleteStacks({ environment: environmentEast }), + deleteStacks({ environment: environmentWest }), + ]); + + await Promise.all([ + deleteS3Buckets({ environment: environmentEast }), + deleteS3Buckets({ environment: environmentWest }), + ]); + + const webClientTerraformDestroy = exec( + `cd web-client/terraform/main && ../bin/environment-destroy.sh ${environmentName}`, + ); + + webClientTerraformDestroy.stdout.on('data', function (data) { + console.log('Web Client Terraform stdout: ', data.toString()); + }); + + webClientTerraformDestroy.stderr.on('data', function (data) { + console.log('Web Client Terraform stderr: ', data.toString()); + }); + + webClientTerraformDestroy.on('exit', function (code) { + console.log( + 'Web Client Terraform child process exited with code ', + code.toString(), + ); + }); + + const webApiTerraformDestroy = exec( + `cd web-api/terraform/main && ../bin/environment-destroy.sh ${environmentName}`, + ); + + webApiTerraformDestroy.stdout.on('data', function (data) { + console.log('Web API Terraform stdout: ', data.toString()); + }); + + webApiTerraformDestroy.stderr.on('data', function (data) { + console.log('Web API Terraform stderr: ', data.toString()); + }); + + webApiTerraformDestroy.on('exit', function (code) { + console.log( + 'Web Client API child process exited with code ', + code.toString(), + ); + }); +}; + +teardownEnvironment(); diff --git a/env-manager/getApiGateway.js b/env-destroy-utils/getApiGateway.js similarity index 100% rename from env-manager/getApiGateway.js rename to env-destroy-utils/getApiGateway.js diff --git a/env-manager/getCloudFormation.js b/env-destroy-utils/getCloudFormation.js similarity index 100% rename from env-manager/getCloudFormation.js rename to env-destroy-utils/getCloudFormation.js diff --git a/env-manager/getCustomDomains.js b/env-destroy-utils/getCustomDomains.js similarity index 100% rename from env-manager/getCustomDomains.js rename to env-destroy-utils/getCustomDomains.js diff --git a/env-manager/getS3.js b/env-destroy-utils/getS3.js similarity index 100% rename from env-manager/getS3.js rename to env-destroy-utils/getS3.js diff --git a/env-manager/getS3Buckets.js b/env-destroy-utils/getS3Buckets.js similarity index 100% rename from env-manager/getS3Buckets.js rename to env-destroy-utils/getS3Buckets.js diff --git a/env-manager/getStacks.js b/env-destroy-utils/getStacks.js similarity index 100% rename from env-manager/getStacks.js rename to env-destroy-utils/getStacks.js diff --git a/env-manager/sleep.js b/env-destroy-utils/sleep.js similarity index 61% rename from env-manager/sleep.js rename to env-destroy-utils/sleep.js index 973b200be66..7a2ec9ffa8e 100644 --- a/env-manager/sleep.js +++ b/env-destroy-utils/sleep.js @@ -1,3 +1,3 @@ -exports.sleep = ms => { +exports.sleepForMilliseconds = ms => { return new Promise(resolve => setTimeout(resolve, ms)); }; diff --git a/env-manager/deleteCognitoPools.js b/env-manager/deleteCognitoPools.js deleted file mode 100644 index f69702e00c2..00000000000 --- a/env-manager/deleteCognitoPools.js +++ /dev/null @@ -1,27 +0,0 @@ -const { exec } = require('child_process'); -const { getCognito } = require('./getCognito'); -const { getCognitoPools } = require('./getCognitoPools'); -const { sleep } = require('./sleep'); - -exports.deleteCognitoPools = async ({ environment }) => { - const cognito = getCognito({ environment }); - - const pools = await getCognitoPools({ - cognito, - environment, - }); - for (const pool of pools) { - console.log('Delete domains for Cognito Pools:', pool); - // the delete-user-pool-domain function isn't in the SDK! - exec( - `aws cognito-idp delete-user-pool-domain --domain auth-${environment.name}-flexion-efcms --region ${environment.region} --user-pool-id ${pool.Id}`, - ); - exec( - `aws cognito-idp delete-user-pool-domain --domain auth-irs-${environment.name}-flexion-efcms --region ${environment.region} --user-pool-id ${pool.Id}`, - ); - await sleep(5000); - console.log('Delete Cognito Pool:', pool.Name); - await cognito.deleteUserPool({ UserPoolId: pool.Id }).promise(); - await sleep(5000); - } -}; diff --git a/env-manager/deleteDynamoDBTables.js b/env-manager/deleteDynamoDBTables.js deleted file mode 100644 index ea9f0e12753..00000000000 --- a/env-manager/deleteDynamoDBTables.js +++ /dev/null @@ -1,17 +0,0 @@ -const { getDynamoDB } = require('./getDynamoDB'); -const { getDynamoDBTables } = require('./getDynamoDBTables'); -const { sleep } = require('./sleep'); - -exports.deleteDynamoDBTables = async ({ environment }) => { - const dynamoDB = getDynamoDB({ environment }); - - const tables = await getDynamoDBTables({ - dynamoDB, - environment, - }); - for (const table of tables) { - console.log('Delete DynamoDB Table:', table); - await dynamoDB.deleteTable({ TableName: table }).promise(); - await sleep(100); - } -}; diff --git a/env-manager/getCognito.js b/env-manager/getCognito.js deleted file mode 100644 index b609c6fab30..00000000000 --- a/env-manager/getCognito.js +++ /dev/null @@ -1,13 +0,0 @@ -const { CognitoIdentityServiceProvider } = require('aws-sdk'); - -exports.getCognito = ({ environment }) => { - const cognito = new CognitoIdentityServiceProvider({ - accessKeyId: environment.accessKeyId, - apiVersion: 'latest', - credentials: environment.credentials, - region: environment.region, - secretAccessKey: environment.secretAccessKey, - }); - - return cognito; -}; diff --git a/env-manager/getCognitoPools.js b/env-manager/getCognitoPools.js deleted file mode 100644 index 7185ad0023b..00000000000 --- a/env-manager/getCognitoPools.js +++ /dev/null @@ -1,10 +0,0 @@ -const { filter } = require('lodash'); - -exports.getCognitoPools = async ({ cognito, environment }) => { - const { UserPools } = await cognito - .listUserPools({ MaxResults: 10 }) - .promise(); - return filter(UserPools, pool => { - return pool.Name.includes(`-${environment.name}`); - }); -}; diff --git a/env-manager/getDynamoDB.js b/env-manager/getDynamoDB.js deleted file mode 100644 index b5d69059099..00000000000 --- a/env-manager/getDynamoDB.js +++ /dev/null @@ -1,13 +0,0 @@ -const { DynamoDB } = require('aws-sdk'); - -exports.getDynamoDB = ({ environment }) => { - const dynamoDB = new DynamoDB({ - accessKeyId: environment.accessKeyId, - apiVersion: 'latest', - credentials: environment.credentials, - region: environment.region, - secretAccessKey: environment.secretAccessKey, - }); - - return dynamoDB; -}; diff --git a/env-manager/getDynamoDBTables.js b/env-manager/getDynamoDBTables.js deleted file mode 100644 index bb4b0a4e1fd..00000000000 --- a/env-manager/getDynamoDBTables.js +++ /dev/null @@ -1,8 +0,0 @@ -const { filter } = require('lodash'); - -exports.getDynamoDBTables = async ({ dynamoDB, environment }) => { - const { TableNames } = await dynamoDB.listTables({}).promise(); - return filter(TableNames, table => { - return table.includes(`-${environment.name}`); - }); -}; diff --git a/env-manager/teardownEnvironment.js b/env-manager/teardownEnvironment.js deleted file mode 100644 index 3d872ff253e..00000000000 --- a/env-manager/teardownEnvironment.js +++ /dev/null @@ -1,40 +0,0 @@ -const { deleteCognitoPools } = require('./deleteCognitoPools'); -const { deleteCustomDomains } = require('./deleteCustomDomains'); -const { deleteDynamoDBTables } = require('./deleteDynamoDBTables'); -const { deleteS3Buckets } = require('./deleteS3Buckets'); -const { deleteStacks } = require('./deleteStacks'); - -// TODO: get all values from ENV variables and validate that they exist -const environmentEast = { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - apiVersion: 'latest', - name: 'exp', - region: 'us-east-1', - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, -}; - -const environmentWest = { - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - apiVersion: 'latest', - name: 'exp', - region: 'us-west-1', - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, -}; - -const teardownEnvironment = async () => { - await deleteCustomDomains({ environment: environmentEast }); - await deleteCustomDomains({ environment: environmentWest }); - - await deleteStacks({ environment: environmentEast }); - await deleteStacks({ environment: environmentWest }); - - await deleteDynamoDBTables({ environment: environmentEast }); - await deleteDynamoDBTables({ environment: environmentWest }); - - await deleteCognitoPools({ environment: environmentEast }); - - await deleteS3Buckets({ environment: environmentEast }); - await deleteS3Buckets({ environment: environmentWest }); -}; - -teardownEnvironment(); diff --git a/get-honeybadger-keys.sh b/get-honeybadger-keys.sh index 7f9c983ed1e..836853e8aea 100755 --- a/get-honeybadger-keys.sh +++ b/get-honeybadger-keys.sh @@ -4,7 +4,7 @@ BRANCH=$1 if [[ $BRANCH == 'develop' ]] ; then echo "${CIRCLE_HONEYBADGER_API_KEY_DEV}" -elif [[ $BRANCH == 'experimental' ]] ; then +elif [[ $BRANCH == 'experimental1' ]] ; then echo "" elif [[ $BRANCH == 'master' ]] ; then echo "" diff --git a/package-lock.json b/package-lock.json index f94d2ed63f6..440c3fa73d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,9 +53,9 @@ } }, "@babel/cli": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.8.4.tgz", - "integrity": "sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.10.1.tgz", + "integrity": "sha512-cVB+dXeGhMOqViIaZs3A9OUAe4pKw4SBNdMw6yHJMYR7s4TB+Cei7ThquV/84O19PdIFWuwe03vxxES0BHUm5g==", "dev": true, "requires": { "chokidar": "^2.1.8", @@ -70,39 +70,39 @@ } }, "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", + "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.10.1" } }, "@babel/compat-data": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.6.tgz", - "integrity": "sha512-5QPTrNen2bm7RBc7dsOmcA5hbrS4O2Vhmk5XOL4zWW/zD/hV0iinpefDlkm+tBBy8kDtFaaeEvmAqt+nURAV2g==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.1.tgz", + "integrity": "sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw==", "dev": true, "requires": { - "browserslist": "^4.11.1", + "browserslist": "^4.12.0", "invariant": "^2.2.4", "semver": "^5.5.0" } }, "@babel/core": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", - "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.6", - "@babel/parser": "^7.9.6", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.2.tgz", + "integrity": "sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.1", + "@babel/generator": "^7.10.2", + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helpers": "^7.10.1", + "@babel/parser": "^7.10.2", + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -131,368 +131,402 @@ } }, "@babel/generator": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz", - "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", + "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", "dev": true, "requires": { - "@babel/types": "^7.9.6", + "@babel/types": "^7.10.2", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", - "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz", + "integrity": "sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", - "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz", + "integrity": "sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-explode-assignable-expression": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-builder-react-jsx": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz", - "integrity": "sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.1.tgz", + "integrity": "sha512-KXzzpyWhXgzjXIlJU1ZjIXzUPdej1suE6vzqgImZ/cpAsR/CC8gUcX4EWRmDfWz/cs6HOCPMBIJ3nKoXt3BFuw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/types": "^7.9.0" + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-builder-react-jsx-experimental": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.5.tgz", - "integrity": "sha512-HAagjAC93tk748jcXpZ7oYRZH485RCq/+yEv9SIWezHRPv9moZArTnkUNciUNzvwHUABmiWKlcxJvMcu59UwTg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.1.tgz", + "integrity": "sha512-irQJ8kpQUV3JasXPSFQ+LCCtJSc5ceZrPFVj6TElR6XCHssi3jV8ch3odIrNtjJFRZZVbrOEfJMI79TPU/h1pQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-module-imports": "^7.8.3", - "@babel/types": "^7.9.5" + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-module-imports": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-compilation-targets": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz", - "integrity": "sha512-x2Nvu0igO0ejXzx09B/1fGBxY9NXQlBW2kZsSxCJft+KHN8t9XWzIvFxtPHnBOAXpVsdxZKZFbRUC8TsNKajMw==", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz", + "integrity": "sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA==", "dev": true, "requires": { - "@babel/compat-data": "^7.9.6", - "browserslist": "^4.11.1", + "@babel/compat-data": "^7.10.1", + "browserslist": "^4.12.0", "invariant": "^2.2.4", "levenary": "^1.1.1", "semver": "^5.5.0" } }, + "@babel/helper-create-class-features-plugin": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz", + "integrity": "sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-member-expression-to-functions": "^7.10.1", + "@babel/helper-optimise-call-expression": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1" + } + }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", - "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz", + "integrity": "sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-regex": "^7.8.3", + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-regex": "^7.10.1", "regexpu-core": "^4.7.0" } }, "@babel/helper-define-map": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", - "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz", + "integrity": "sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/types": "^7.8.3", + "@babel/helper-function-name": "^7.10.1", + "@babel/types": "^7.10.1", "lodash": "^4.17.13" } }, "@babel/helper-explode-assignable-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", - "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz", + "integrity": "sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg==", "dev": true, "requires": { - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-function-name": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", - "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz", + "integrity": "sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.9.5" + "@babel/helper-get-function-arity": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz", + "integrity": "sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" } }, "@babel/helper-hoist-variables": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", - "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz", + "integrity": "sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz", + "integrity": "sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" } }, "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz", + "integrity": "sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" } }, "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz", + "integrity": "sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", + "@babel/helper-module-imports": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1", + "@babel/helper-simple-access": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/types": "^7.10.1", "lodash": "^4.17.13" } }, "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz", + "integrity": "sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" } }, "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz", + "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==", "dev": true }, "@babel/helper-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", - "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.1.tgz", + "integrity": "sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g==", "dev": true, "requires": { "lodash": "^4.17.13" } }, "@babel/helper-remap-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", - "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz", + "integrity": "sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-wrap-function": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-wrap-function": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-replace-supers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", - "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz", + "integrity": "sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" + "@babel/helper-member-expression-to-functions": "^7.10.1", + "@babel/helper-optimise-call-expression": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz", + "integrity": "sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/template": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz", + "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.1" } }, "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", "dev": true }, "@babel/helper-wrap-function": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", - "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz", + "integrity": "sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-function-name": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/helpers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", - "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.1.tgz", + "integrity": "sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", + "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.0", + "@babel/helper-validator-identifier": "^7.10.1", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", - "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.2.tgz", + "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", - "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz", + "integrity": "sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-remap-async-to-generator": "^7.10.1", "@babel/plugin-syntax-async-generators": "^7.8.0" } }, + "@babel/plugin-proposal-class-properties": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz", + "integrity": "sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", - "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz", + "integrity": "sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", "@babel/plugin-syntax-dynamic-import": "^7.8.0" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", - "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz", + "integrity": "sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", "@babel/plugin-syntax-json-strings": "^7.8.0" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz", + "integrity": "sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", - "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz", + "integrity": "sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-numeric-separator": "^7.10.1" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz", - "integrity": "sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz", + "integrity": "sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.9.5" + "@babel/plugin-transform-parameters": "^7.10.1" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz", + "integrity": "sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", - "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz", + "integrity": "sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, + "@babel/plugin-proposal-private-methods": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz", + "integrity": "sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", - "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz", + "integrity": "sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.8", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-syntax-async-generators": { @@ -514,12 +548,12 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz", - "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz", + "integrity": "sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-syntax-dynamic-import": { @@ -532,12 +566,21 @@ } }, "@babel/plugin-syntax-flow": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz", - "integrity": "sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.10.1.tgz", + "integrity": "sha512-b3pWVncLBYoPP60UOTc7NMlbtsHQ6ITim78KQejNHK6WJ2mzV5kCcg4mIWpasAfJEgwVTibwo2e+FU7UEIKQUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.1.tgz", + "integrity": "sha512-ypC4jwfIVF72og0dgvEcFRdOM2V9Qm1tu7RGmdZOlhsccyK0wisXmMObGuWEOd5jQ+K9wcIgSNftCpk2vkjUfQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-syntax-json-strings": { @@ -550,21 +593,21 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz", - "integrity": "sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.1.tgz", + "integrity": "sha512-+OxyOArpVFXQeXKLO9o+r2I4dIoVoy6+Uu0vKELrlweDM3QJADZj+Z+5ERansZqIZBcLj42vHnDI8Rz9BnRIuQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz", - "integrity": "sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.1.tgz", + "integrity": "sha512-XyHIFa9kdrgJS91CUH+ccPVTnJShr8nLGc5bG2IhGXv5p1Rd+8BleGE5yzIg2Nc1QZAdHDa0Qp4m6066OL96Iw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-syntax-nullish-coalescing-operator": { @@ -577,12 +620,12 @@ } }, "@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz", + "integrity": "sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-syntax-object-rest-spread": { @@ -613,206 +656,206 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", - "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz", + "integrity": "sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", - "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz", + "integrity": "sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", - "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz", + "integrity": "sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-remap-async-to-generator": "^7.8.3" + "@babel/helper-module-imports": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-remap-async-to-generator": "^7.10.1" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", - "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz", + "integrity": "sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", - "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz", + "integrity": "sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.1", "lodash": "^4.17.13" } }, "@babel/plugin-transform-classes": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz", - "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-define-map": "^7.8.3", - "@babel/helper-function-name": "^7.9.5", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-split-export-declaration": "^7.8.3", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz", + "integrity": "sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-define-map": "^7.10.1", + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-optimise-call-expression": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", - "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz", + "integrity": "sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-destructuring": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz", - "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz", + "integrity": "sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", - "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz", + "integrity": "sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", - "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz", + "integrity": "sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", - "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz", + "integrity": "sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-flow-strip-types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz", - "integrity": "sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.10.1.tgz", + "integrity": "sha512-i4o0YwiJBIsIx7/liVCZ3Q2WkWr1/Yu39PksBOnh/khW2SwIFsGa5Ze+MSon5KbDfrEHP9NeyefAgvUSXzaEkw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-flow": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-flow": "^7.10.1" } }, "@babel/plugin-transform-for-of": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", - "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz", + "integrity": "sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", - "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz", + "integrity": "sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", - "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz", + "integrity": "sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", - "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz", + "integrity": "sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz", - "integrity": "sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz", + "integrity": "sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz", - "integrity": "sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz", + "integrity": "sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-simple-access": "^7.10.1", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz", - "integrity": "sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz", + "integrity": "sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.8.3", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-hoist-variables": "^7.10.1", + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz", - "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz", + "integrity": "sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-named-capturing-groups-regex": { @@ -825,232 +868,255 @@ } }, "@babel/plugin-transform-new-target": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", - "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz", + "integrity": "sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-object-super": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", - "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz", + "integrity": "sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1" } }, "@babel/plugin-transform-parameters": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz", - "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz", + "integrity": "sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-get-function-arity": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-property-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", - "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz", + "integrity": "sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz", - "integrity": "sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.1.tgz", + "integrity": "sha512-rBjKcVwjk26H3VX8pavMxGf33LNlbocMHdSeldIEswtQ/hrjyTG8fKKILW1cSkODyRovckN/uZlGb2+sAV9JUQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.4.tgz", - "integrity": "sha512-Mjqf3pZBNLt854CK0C/kRuXAnE6H/bo7xYojP+WGtX8glDGSibcwnsWwhwoSuRg0+EBnxPC1ouVnuetUIlPSAw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.1.tgz", + "integrity": "sha512-MBVworWiSRBap3Vs39eHt+6pJuLUAaK4oxGc8g+wY+vuSJvLiEQjW1LSTqKb8OUPtDvHCkdPhk7d6sjC19xyFw==", "dev": true, "requires": { - "@babel/helper-builder-react-jsx": "^7.9.0", - "@babel/helper-builder-react-jsx-experimental": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.8.3" + "@babel/helper-builder-react-jsx": "^7.10.1", + "@babel/helper-builder-react-jsx-experimental": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.9.0.tgz", - "integrity": "sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.1.tgz", + "integrity": "sha512-XwDy/FFoCfw9wGFtdn5Z+dHh6HXKHkC6DwKNWpN74VWinUagZfDcEJc3Y8Dn5B3WMVnAllX8Kviaw7MtC5Epwg==", "dev": true, "requires": { - "@babel/helper-builder-react-jsx-experimental": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.8.3" + "@babel/helper-builder-react-jsx-experimental": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" } }, "@babel/plugin-transform-react-jsx-self": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.9.0.tgz", - "integrity": "sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.1.tgz", + "integrity": "sha512-4p+RBw9d1qV4S749J42ZooeQaBomFPrSxa9JONLHJ1TxCBo3TzJ79vtmG2S2erUT8PDDrPdw4ZbXGr2/1+dILA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" } }, "@babel/plugin-transform-react-jsx-source": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz", - "integrity": "sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.1.tgz", + "integrity": "sha512-neAbaKkoiL+LXYbGDvh6PjPG+YeA67OsZlE78u50xbWh2L1/C81uHiNP5d1fw+uqUIoiNdCC8ZB+G4Zh3hShJA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz", + "integrity": "sha512-mfhoiai083AkeewsBHUpaS/FM1dmUENHBMpS/tugSJ7VXqXO5dCN1Gkint2YvM1Cdv1uhmAKt1ZOuAjceKmlLA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.8.3" + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-regenerator": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", - "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz", + "integrity": "sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", - "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz", + "integrity": "sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", - "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz", + "integrity": "sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", - "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz", + "integrity": "sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", - "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz", + "integrity": "sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-regex": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-regex": "^7.10.1" } }, "@babel/plugin-transform-template-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", - "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz", + "integrity": "sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", - "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz", + "integrity": "sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz", + "integrity": "sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", - "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz", + "integrity": "sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" } }, "@babel/preset-env": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.6.tgz", - "integrity": "sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.9.6", - "@babel/helper-compilation-targets": "^7.9.6", - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-proposal-async-generator-functions": "^7.8.3", - "@babel/plugin-proposal-dynamic-import": "^7.8.3", - "@babel/plugin-proposal-json-strings": "^7.8.3", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-proposal-numeric-separator": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.9.6", - "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", - "@babel/plugin-proposal-optional-chaining": "^7.9.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.2.tgz", + "integrity": "sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.10.1", + "@babel/helper-compilation-targets": "^7.10.2", + "@babel/helper-module-imports": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-proposal-async-generator-functions": "^7.10.1", + "@babel/plugin-proposal-class-properties": "^7.10.1", + "@babel/plugin-proposal-dynamic-import": "^7.10.1", + "@babel/plugin-proposal-json-strings": "^7.10.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.1", + "@babel/plugin-proposal-numeric-separator": "^7.10.1", + "@babel/plugin-proposal-object-rest-spread": "^7.10.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.10.1", + "@babel/plugin-proposal-optional-chaining": "^7.10.1", + "@babel/plugin-proposal-private-methods": "^7.10.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.10.1", "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.10.1", "@babel/plugin-syntax-dynamic-import": "^7.8.0", "@babel/plugin-syntax-json-strings": "^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.1", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.8.3", - "@babel/plugin-transform-async-to-generator": "^7.8.3", - "@babel/plugin-transform-block-scoped-functions": "^7.8.3", - "@babel/plugin-transform-block-scoping": "^7.8.3", - "@babel/plugin-transform-classes": "^7.9.5", - "@babel/plugin-transform-computed-properties": "^7.8.3", - "@babel/plugin-transform-destructuring": "^7.9.5", - "@babel/plugin-transform-dotall-regex": "^7.8.3", - "@babel/plugin-transform-duplicate-keys": "^7.8.3", - "@babel/plugin-transform-exponentiation-operator": "^7.8.3", - "@babel/plugin-transform-for-of": "^7.9.0", - "@babel/plugin-transform-function-name": "^7.8.3", - "@babel/plugin-transform-literals": "^7.8.3", - "@babel/plugin-transform-member-expression-literals": "^7.8.3", - "@babel/plugin-transform-modules-amd": "^7.9.6", - "@babel/plugin-transform-modules-commonjs": "^7.9.6", - "@babel/plugin-transform-modules-systemjs": "^7.9.6", - "@babel/plugin-transform-modules-umd": "^7.9.0", + "@babel/plugin-syntax-top-level-await": "^7.10.1", + "@babel/plugin-transform-arrow-functions": "^7.10.1", + "@babel/plugin-transform-async-to-generator": "^7.10.1", + "@babel/plugin-transform-block-scoped-functions": "^7.10.1", + "@babel/plugin-transform-block-scoping": "^7.10.1", + "@babel/plugin-transform-classes": "^7.10.1", + "@babel/plugin-transform-computed-properties": "^7.10.1", + "@babel/plugin-transform-destructuring": "^7.10.1", + "@babel/plugin-transform-dotall-regex": "^7.10.1", + "@babel/plugin-transform-duplicate-keys": "^7.10.1", + "@babel/plugin-transform-exponentiation-operator": "^7.10.1", + "@babel/plugin-transform-for-of": "^7.10.1", + "@babel/plugin-transform-function-name": "^7.10.1", + "@babel/plugin-transform-literals": "^7.10.1", + "@babel/plugin-transform-member-expression-literals": "^7.10.1", + "@babel/plugin-transform-modules-amd": "^7.10.1", + "@babel/plugin-transform-modules-commonjs": "^7.10.1", + "@babel/plugin-transform-modules-systemjs": "^7.10.1", + "@babel/plugin-transform-modules-umd": "^7.10.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", - "@babel/plugin-transform-new-target": "^7.8.3", - "@babel/plugin-transform-object-super": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.9.5", - "@babel/plugin-transform-property-literals": "^7.8.3", - "@babel/plugin-transform-regenerator": "^7.8.7", - "@babel/plugin-transform-reserved-words": "^7.8.3", - "@babel/plugin-transform-shorthand-properties": "^7.8.3", - "@babel/plugin-transform-spread": "^7.8.3", - "@babel/plugin-transform-sticky-regex": "^7.8.3", - "@babel/plugin-transform-template-literals": "^7.8.3", - "@babel/plugin-transform-typeof-symbol": "^7.8.4", - "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.10.1", + "@babel/plugin-transform-object-super": "^7.10.1", + "@babel/plugin-transform-parameters": "^7.10.1", + "@babel/plugin-transform-property-literals": "^7.10.1", + "@babel/plugin-transform-regenerator": "^7.10.1", + "@babel/plugin-transform-reserved-words": "^7.10.1", + "@babel/plugin-transform-shorthand-properties": "^7.10.1", + "@babel/plugin-transform-spread": "^7.10.1", + "@babel/plugin-transform-sticky-regex": "^7.10.1", + "@babel/plugin-transform-template-literals": "^7.10.1", + "@babel/plugin-transform-typeof-symbol": "^7.10.1", + "@babel/plugin-transform-unicode-escapes": "^7.10.1", + "@babel/plugin-transform-unicode-regex": "^7.10.1", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.9.6", - "browserslist": "^4.11.1", + "@babel/types": "^7.10.2", + "browserslist": "^4.12.0", "core-js-compat": "^3.6.2", "invariant": "^2.2.2", "levenary": "^1.1.1", @@ -1071,23 +1137,24 @@ } }, "@babel/preset-react": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.4.tgz", - "integrity": "sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.10.1.tgz", + "integrity": "sha512-Rw0SxQ7VKhObmFjD/cUcKhPTtzpeviEFX1E6PgP+cYOhQ98icNqtINNFANlsdbQHrmeWnqdxA4Tmnl1jy5tp3Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-transform-react-display-name": "^7.8.3", - "@babel/plugin-transform-react-jsx": "^7.9.4", - "@babel/plugin-transform-react-jsx-development": "^7.9.0", - "@babel/plugin-transform-react-jsx-self": "^7.9.0", - "@babel/plugin-transform-react-jsx-source": "^7.9.0" + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-transform-react-display-name": "^7.10.1", + "@babel/plugin-transform-react-jsx": "^7.10.1", + "@babel/plugin-transform-react-jsx-development": "^7.10.1", + "@babel/plugin-transform-react-jsx-self": "^7.10.1", + "@babel/plugin-transform-react-jsx-source": "^7.10.1", + "@babel/plugin-transform-react-pure-annotations": "^7.10.1" } }, "@babel/register": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.9.0.tgz", - "integrity": "sha512-Tv8Zyi2J2VRR8g7pC5gTeIN8Ihultbmk0ocyNz8H2nEZbmhp1N6q0A1UGsQbDvGP/sNinQKUHf3SqXwqjtFv4Q==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.10.1.tgz", + "integrity": "sha512-sl96+kB3IA2B9EzpwwBmYadOT14vw3KaXOknGDbJaZCOj52GDA4Tivudq9doCJcB+bEIKCEARZYwRgBBsCGXyg==", "dev": true, "requires": { "find-cache-dir": "^2.0.0", @@ -1098,18 +1165,18 @@ } }, "@babel/runtime": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", - "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.6.tgz", - "integrity": "sha512-6toWAfaALQjt3KMZQc6fABqZwUDDuWzz+cAfPhqyEnzxvdWOAkjwPNxgF8xlmo7OWLsSjaKjsskpKHRLaMArOA==", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.2.tgz", + "integrity": "sha512-+a2M/u7r15o3dV1NEizr9bRi+KUVnrs/qYxF0Z06DAPx/4VCWaz1WA7EcbE+uqGgt39lp5akWGmHsTseIkHkHg==", "dev": true, "requires": { "core-js-pure": "^3.0.0", @@ -1117,28 +1184,28 @@ } }, "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.1.tgz", + "integrity": "sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" + "@babel/code-frame": "^7.10.1", + "@babel/parser": "^7.10.1", + "@babel/types": "^7.10.1" } }, "@babel/traverse": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz", - "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.1.tgz", + "integrity": "sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-function-name": "^7.9.5", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", + "@babel/code-frame": "^7.10.1", + "@babel/generator": "^7.10.1", + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1", + "@babel/parser": "^7.10.1", + "@babel/types": "^7.10.1", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -1162,12 +1229,12 @@ } }, "@babel/types": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", - "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", + "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.5", + "@babel/helper-validator-identifier": "^7.10.1", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } @@ -1194,14 +1261,11 @@ "dev": true, "requires": { "exec-sh": "^0.3.2", - "minimist": "^1.2.0" + "minimist": "^1.2.5" }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } }, @@ -1450,9 +1514,9 @@ } }, "@fortawesome/react-fontawesome": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.9.tgz", - "integrity": "sha512-49V3WNysLZU5fZ3sqSuys4nGRytsrxJktbv3vuaXkEoxv22C6T7TEG0TW6+nqVjMnkfCQd5xOnmJoZHMF78tOw==", + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.10.tgz", + "integrity": "sha512-UGdpJiLBIqR/8xcLrCarf2pChqQFKjDTD02C7ZS/odpOVVl2YuHYRCLEOQ0GpfOk6jtYhmouSFOFoC8qNCe5cg==", "dev": true, "requires": { "prop-types": "^15.7.2" @@ -2349,9 +2413,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2466,9 +2530,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2650,9 +2714,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2790,9 +2854,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2900,9 +2964,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2947,6 +3011,55 @@ "integrity": "sha512-qS/a24RA5FEoiJS9wiv6Pwg2c/kiUo3IVUQcfeM9JvsR6pM8Yx+yl/6xWYLckZCT5jpLNhslgjiA8p/XcGyMRQ==", "dev": true }, + "@koa/router": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-9.0.1.tgz", + "integrity": "sha512-OI+OU49CJV4px0WkIMmayBeqVXB/JS1ZMq7UoGlTZt6Y7ijK7kdeQ18+SEHHJPytmtI1y6Hf8XLrpxva3mhv5Q==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "http-errors": "^1.7.3", + "koa-compose": "^4.1.0", + "methods": "^1.1.2", + "path-to-regexp": "^6.1.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "path-to-regexp": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.1.0.tgz", + "integrity": "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==", + "dev": true + } + } + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -3142,6 +3255,70 @@ } } }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", + "dev": true + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", + "dev": true + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "dev": true, + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", + "dev": true + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", + "dev": true + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", + "dev": true + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", + "dev": true + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", + "dev": true + }, "@samverschueren/stream-to-observable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", @@ -3152,19 +3329,20 @@ } }, "@serverless/cli": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@serverless/cli/-/cli-1.4.0.tgz", - "integrity": "sha512-YqlCiYmRFeGksw6XJaXbigIDlktc7OfRuVpyPB7IZgkCJ9mUlBmvyWdwqJEQdkUz0xPTGsd4Jd/XSrwyiw1Brg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@serverless/cli/-/cli-1.5.0.tgz", + "integrity": "sha512-hGYbsI/Gf9RFTTZS4yXvufDAv/lk6ae8PGBbjZySEtlx/zqA31PgNaWBqWjCo3+KMfpcq8abDaxiJbsu4OfWew==", "dev": true, "requires": { - "@serverless/core": "^1.0.0", - "@serverless/template": "^1.1.0", - "ansi-escapes": "^4.2.0", + "@serverless/core": "^1.1.2", + "@serverless/template": "^1.1.3", + "@serverless/utils": "^1.1.0", + "ansi-escapes": "^4.3.1", "chalk": "^2.4.2", - "chokidar": "^3.0.2", - "dotenv": "^8.0.0", - "figures": "^3.0.0", - "minimist": "^1.2.0", + "chokidar": "^3.4.0", + "dotenv": "^8.2.0", + "figures": "^3.2.0", + "minimist": "^1.2.5", "prettyoutput": "^1.2.0", "strip-ansi": "^5.2.0" }, @@ -3281,10 +3459,7 @@ "dev": true }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" }, "readdirp": { "version": "3.4.0", @@ -3372,14 +3547,14 @@ } }, "@serverless/enterprise-plugin": { - "version": "3.6.12", - "resolved": "https://registry.npmjs.org/@serverless/enterprise-plugin/-/enterprise-plugin-3.6.12.tgz", - "integrity": "sha512-AKfVXBQefJV0RIg3YpDlF9Vep3aMzjpD6lP+lgSVKUVUMEaZnp4x9Y40wc+O7pwAmowM033sFxoIkADEofv1Sg==", + "version": "3.6.13", + "resolved": "https://registry.npmjs.org/@serverless/enterprise-plugin/-/enterprise-plugin-3.6.13.tgz", + "integrity": "sha512-L7DOqo7LviAbYIySV5n+sURwyI5rf6IfVKRzFF3CK/EtbHUBJn7gEdHNo86Xmsm4I251ZggGSsorQvSIzT2VUQ==", "dev": true, "requires": { "@serverless/event-mocks": "^1.1.1", - "@serverless/platform-client": "^0.25.7", - "@serverless/platform-sdk": "^2.3.0", + "@serverless/platform-client": "^0.25.14", + "@serverless/platform-sdk": "^2.3.1", "chalk": "^2.4.2", "child-process-ext": "^2.1.1", "chokidar": "^3.4.0", @@ -3390,12 +3565,12 @@ "fs-extra": "^8.1.0", "iso8601-duration": "^1.2.0", "isomorphic-fetch": "^2.2.1", - "js-yaml": "^3.13.1", + "js-yaml": "^3.14.0", "jsonata": "^1.8.3", "jszip": "^3.4.0", "lodash": "^4.17.15", "memoizee": "^0.4.14", - "moment": "^2.25.3", + "moment": "^2.26.0", "node-dir": "^0.1.17", "node-fetch": "^2.6.0", "regenerator-runtime": "^0.13.5", @@ -3403,7 +3578,7 @@ "simple-git": "^1.132.0", "source-map-support": "^0.5.19", "update-notifier": "^2.5.0", - "uuid": "^8.0.0", + "uuid": "^3.4.0", "yamljs": "^0.3.0" }, "dependencies": { @@ -3517,6 +3692,22 @@ "requires": { "is-number": "^7.0.0" } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + } } } }, @@ -3683,23 +3874,26 @@ } }, "@serverless/platform-client-china": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@serverless/platform-client-china/-/platform-client-china-1.0.14.tgz", - "integrity": "sha512-cuNkZA551XiaU2fD9ZVjcnqN5L3HvRpLf8JAGNJJ3fe8iBHDLjs+kVCRI/LOF7lUAIcElOvcFeqmsg8GFbbI9g==", + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/@serverless/platform-client-china/-/platform-client-china-1.0.18.tgz", + "integrity": "sha512-M6bucQXgnHF6M+4D4Wt8KCz1bUxpMtG8CPS/WiOhzwwTdG79aYwNIOIC1xo0wtDmrlTwh8ECD8WclAWSHmfaqg==", "dev": true, "requires": { - "@serverless/utils-china": "^0.1.9", + "@serverless/utils-china": "^0.1.13", "archiver": "^4.0.1", "dotenv": "^8.2.0", + "fs-extra": "^8.1.0", "https-proxy-agent": "^5.0.0", "isomorphic-ws": "^4.0.1", - "js-yaml": "^3.13.1", + "js-yaml": "^3.14.0", "jwt-decode": "^2.2.0", "minimatch": "^3.0.4", + "pify": "^5.0.0", "querystring": "^0.2.0", + "stream.pipeline-shim": "^1.1.0", "traverse": "^0.6.6", "urlencode": "^1.1.0", - "ws": "^7.2.1" + "ws": "^7.3.0" }, "dependencies": { "agent-base": { @@ -3786,6 +3980,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -3817,9 +4017,9 @@ } }, "@serverless/platform-sdk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@serverless/platform-sdk/-/platform-sdk-2.3.0.tgz", - "integrity": "sha512-+9TiMYDVKJOyDWg9p/k0kmGVZ3+rjB8DXpACDxxyUChDSsRS55CTJnt321Yx7APfHctNRSnv3ubYmx7oGSTETQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@serverless/platform-sdk/-/platform-sdk-2.3.1.tgz", + "integrity": "sha512-EiSizya9bK0+5uae3GH9uXuWAchZplkLO0tWOAXtnU5QWSg5zicANL9jKCw0dyhjUOvbcO0ddhFlG8EGYvJFSA==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -3912,19 +4112,59 @@ "traverse": "^0.6.6" } }, + "@serverless/utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@serverless/utils/-/utils-1.1.0.tgz", + "integrity": "sha512-MZBLphb8Dz9/mGclFQ53INznSFHZAwS2z4H8RZb6UPCqcRhW0SRrdLwLmn9JIqLWH4Zn95LbNsAjmzJ4Dl3CPQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "lodash": "^4.17.15", + "rc": "^1.2.8", + "type": "^2.0.0", + "uuid": "^3.4.0", + "write-file-atomic": "^2.4.3" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, "@serverless/utils-china": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@serverless/utils-china/-/utils-china-0.1.9.tgz", - "integrity": "sha512-H1sryCXyWryvHUl8nhgWXmFDfvixWER0y5A7gcpJzduwmCJOMaV3plg9MIrvS27tNg3c/RVQ0uKNkx1NlOFgJA==", + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/@serverless/utils-china/-/utils-china-0.1.14.tgz", + "integrity": "sha512-M1/bvs/ZhqPfj1yqdsyxu4yIWtCXHd+a5Av/ZXef1deTn1zJHO+ZJdOtvFzWHb0Sr7sX4p2ZHD/vGj0Rve/Pzg==", "dev": true, "requires": { - "@tencent-sdk/capi": "^0.2.15-alpha.0", + "@tencent-sdk/capi": "0.2.15-alpha.0", "dijkstrajs": "^1.0.1", "dot-qs": "0.2.0", "duplexify": "^4.1.1", "end-of-stream": "^1.4.4", "https-proxy-agent": "^5.0.0", "object-assign": "^4.1.1", + "protobufjs": "^6.9.0", "socket.io-client": "^2.3.0", "socket.io-stream": "^0.9.1", "winston": "^3.2.1" @@ -4074,15 +4314,69 @@ } }, "@tencent-sdk/capi": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/@tencent-sdk/capi/-/capi-0.2.15.tgz", - "integrity": "sha512-5t94Mo/+Kdvr60tJR/+pylUCwIM+ipcBIkUi4M7dtV0yCpuykOXV4GYT1aWg/iWMXyIPnfOUk4Pr6OwDoAVehw==", + "version": "0.2.15-alpha.0", + "resolved": "https://registry.npmjs.org/@tencent-sdk/capi/-/capi-0.2.15-alpha.0.tgz", + "integrity": "sha512-1P3tlXJgQaQIqphh1jVeyXDOajFUFIU4J7MoU3Pwxdx58dOwS59/suTq4El0Fe12pc3Gmhw+eDeQc35GaHYViw==", "dev": true, "requires": { + "chalk": "^3.0.0", + "moment": "^2.24.0", "object-assign": "^4.1.1", "querystring": "^0.2.0", "request": "^2.88.0", "request-promise-native": "^1.0.8" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "@types/babel-types": { @@ -4092,9 +4386,9 @@ "dev": true }, "@types/babel__core": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", - "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.8.tgz", + "integrity": "sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -4124,9 +4418,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.11.tgz", - "integrity": "sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.12.tgz", + "integrity": "sha512-t4CoEokHTfcyfb4hUaF9oOHu9RmmNWnm1CP0YmMqOOfClKascOmvlEM736vlqeScuGvBDsHkf8R2INd4DWreQA==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -4175,12 +4469,6 @@ "@types/node": "*" } }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, "@types/express": { "version": "4.17.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", @@ -4205,12 +4493,11 @@ } }, "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA==", "dev": true, "requires": { - "@types/events": "*", "@types/minimatch": "*", "@types/node": "*" } @@ -4234,9 +4521,9 @@ } }, "@types/istanbul-lib-coverage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz", - "integrity": "sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", "dev": true }, "@types/istanbul-lib-report": { @@ -4259,15 +4546,27 @@ } }, "@types/json-schema": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", - "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, "@types/lodash": { - "version": "4.14.149", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", - "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==", + "version": "4.14.155", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.155.tgz", + "integrity": "sha512-vEcX7S7aPhsBCivxMwAANQburHBtfN9RdyXFk84IJmu2Z4Hkg1tOFgaslRiEqqvoLtbCBi6ika1EMspE+NZ9Lg==", + "dev": true + }, + "@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==", "dev": true }, "@types/mime": { @@ -4289,18 +4588,18 @@ "dev": true }, "@types/mysql": { - "version": "2.15.11", - "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.11.tgz", - "integrity": "sha512-8fpdwdOKdmLgbnwVqPtYJGiCOLaxIsjx36zWYtP9b29M0644MSiZJNC54AaCNLbtsAKXcj2uJUlQh/bYjUkwaw==", + "version": "2.15.13", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.13.tgz", + "integrity": "sha512-9hlUjghm0EeLndks56bH6NDcA52ozGcLXr8etlJVuoYXHa2M+neR2XpGekrulln5LsZg8ZRp8/Z0qKGb9Mpjbw==", "dev": true, "requires": { "@types/node": "*" } }, "@types/node": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.4.tgz", - "integrity": "sha512-k3NqigXWRzQZVBDS5D1U70A5E8Qk4Kh+Ha/x4M8Bt9pF0X05eggfnC9+63Usc9Q928hRUIpIhTQaXsZwZBl4Ew==", + "version": "14.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", + "integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==", "dev": true }, "@types/normalize-package-data": { @@ -4332,9 +4631,9 @@ "dev": true }, "@types/prettier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.0.tgz", - "integrity": "sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.1.tgz", + "integrity": "sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ==", "dev": true }, "@types/q": { @@ -4679,9 +4978,9 @@ } }, "acorn": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", "dev": true }, "acorn-globals": { @@ -4701,9 +5000,9 @@ "dev": true }, "acorn-node": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-2.0.0.tgz", - "integrity": "sha512-BKPICDOEXUF/q/ltRrXLW7QGi+06r68BSQr4BNFZnY8L04KsYRttFq7SDsCdBEwfuGVdBV9AgaQtUkAbHg5Qnw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-2.0.1.tgz", + "integrity": "sha512-VLR5sHqjk+8c5hrKeP2fWaIHb8eewsoxnZ8r2qpwRHXMHuC7KyOPflnOx9dLssVQUurzJ7rO0OzIFjHcndafWw==", "dev": true, "requires": { "acorn": "^7.0.0", @@ -5307,12 +5606,12 @@ "dev": true }, "aws-sdk": { - "version": "2.681.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.681.0.tgz", - "integrity": "sha512-/p8CDJ7LZvB1i4WrJrb32FUbbPdiZFZSN6FI2lv7s/scKypmuv/iJ9kpx6QWSWQZ72kJ3Njk/0o7GuVlw0jHXw==", + "version": "2.697.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.697.0.tgz", + "integrity": "sha512-aNrwiPRHQyzjJxpfgLwVOevuGTOMkU5uiP4VDIngfc/k4s2kQgLSyhLSKmNTjbubHCHfs1sQQkP3RXK2Oi8Rbw==", "dev": true, "requires": { - "buffer": "4.9.1", + "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", "jmespath": "0.15.0", @@ -5324,9 +5623,9 @@ }, "dependencies": { "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, "requires": { "base64-js": "^1.0.2", @@ -5416,15 +5715,15 @@ } }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", "dev": true }, "axe-core": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.3.tgz", - "integrity": "sha512-HZpLE7xu05+8AbpqXITGdxp1Xwk8ysAXrg7MiKRY27py3DAyEJpoJQo1727pWF3F+O79V3r+cTWhOzfB49P89w==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.4.tgz", + "integrity": "sha512-JRuxixN5bPHre+815qnyqBQzNpRTqGxLWflvjr4REpGZ5o0WXm+ik2IS4PZ01EnacWmVRB4jCPWFiYENMiiasA==", "dev": true }, "axios": { @@ -5483,9 +5782,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -5611,14 +5910,15 @@ "dev": true }, "babel-preset-current-node-syntax": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz", - "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz", + "integrity": "sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ==", "dev": true, "requires": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -6539,6 +6839,15 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "dev": true, + "requires": { + "dicer": "0.3.0" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -6728,9 +7037,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001062", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001062.tgz", - "integrity": "sha512-ei9ZqeOnN7edDrb24QfJ0OZicpEbsWxv7WusOiQGz/f2SfvBgHHbOEwBJ8HKGVSyx8Z6ndPjxzR6m0NQq+0bfw==", + "version": "1.0.30001084", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001084.tgz", + "integrity": "sha512-ftdc5oGmhEbLUuMZ/Qp3mOpzfZLCxPYKcvGv6v2dJJ+8EdqcvZRbAGOiLmkM/PV1QGta/uwBs8/nCl6sokDW6w==", "dev": true }, "capture-exit": { @@ -7372,9 +7681,9 @@ "dev": true }, "comment-parser": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.7.4.tgz", - "integrity": "sha512-Nnl77/mt6sj1BiYSVMeMWzvD0183F2MFOJyFRmZHimUVDYS9J40AvXpiFA7RpU5pQH+HkvYc0dnsHpwW2xmbyQ==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.7.5.tgz", + "integrity": "sha512-iH9YA35ccw94nx5244GVkpyC9eVTsL71jZz6iz5w6RIf79JLF2AsXHXq9p6Oaohyl3sx5qSMnGsWUDFIAfWL4w==", "dev": true }, "common-tags": { @@ -8195,9 +8504,9 @@ "dev": true }, "cypress": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-4.6.0.tgz", - "integrity": "sha512-vIPXAceRP+Nxvnm/O9ruY9EQaRGmVVybtk9F1sfC9mH3067YbitrdBTynaaLuHFj90p9e0U2ZCV7OkX4x4V/Wg==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-4.8.0.tgz", + "integrity": "sha512-Lsff8lF8pq6k/ioNua783tCsxGSLp6gqGXecdIfqCkqjYiOA53XKuEf1CaQJLUVs1dHSf49eDUp/sb620oJjVQ==", "dev": true, "requires": { "@cypress/listr-verbose-renderer": "0.4.1", @@ -8226,7 +8535,7 @@ "listr": "0.14.3", "lodash": "4.17.15", "log-symbols": "3.0.0", - "minimist": "1.2.5", + "minimist": "^1.2.5", "moment": "2.24.0", "ospath": "1.2.2", "pretty-bytes": "5.3.0", @@ -8260,6 +8569,9 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "minimist": { + "version": "^1.2.5" + }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", @@ -8345,9 +8657,9 @@ "dev": true }, "dayjs": { - "version": "1.8.27", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.27.tgz", - "integrity": "sha512-Jpa2acjWIeOkg8KURUHICk0EqnEFSSF5eMEscsOgyJ92ZukXwmpmRkPSUka7KHSfbj5eKH30ieosYip+ky9emQ==", + "version": "1.8.28", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.28.tgz", + "integrity": "sha512-ccnYgKC0/hPSGXxj7Ju6AV/BP4HUkXC2u15mikXT5mX9YorEaoi1bEKOmAqdkJHN4EEkmAf97SpH66Try5Mbeg==", "dev": true }, "deasync": { @@ -9001,6 +9313,15 @@ "kuler": "1.0.x" } }, + "dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "dev": true, + "requires": { + "streamsearch": "0.1.2" + } + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -9387,9 +9708,9 @@ } }, "electron-to-chromium": { - "version": "1.3.446", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.446.tgz", - "integrity": "sha512-CLQaFuvkKqR9FD2G3cJrr1fV7DRMXiAKWLP2F8cxtvvtzAS7Tubt0kF47/m+uE61kiT+I7ZEn7HqLnmWdOhmuA==", + "version": "1.3.474", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.474.tgz", + "integrity": "sha512-fPkSgT9IBKmVJz02XioNsIpg0WYmkPrvU1lUJblMMJALxyE7/32NGvbJQKKxpNokozPvqfqkuUqVClYsvetcLw==", "dev": true }, "elegant-spinner": { @@ -9492,9 +9813,9 @@ } }, "engine.io-client": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.2.tgz", - "integrity": "sha512-AWjc1Xg06a6UPFOBAzJf48W1UR/qKYmv/ubgSCumo9GXgvL/xGIvo05dXoBL+2NTLMipDI7in8xK61C17L25xg==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", + "integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", "dev": true, "requires": { "component-emitter": "~1.3.0", @@ -9550,9 +9871,9 @@ } }, "enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz", + "integrity": "sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -9687,22 +10008,22 @@ } }, "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -9819,9 +10140,9 @@ "dev": true }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.2.tgz", + "integrity": "sha512-InuOIiKk8wwuOFg6x9BQXbzjrQhtyXh46K9bqVTPzSo2FnyMBaYGBMC6PhQy7yxxil9vIedFBweQBMK74/7o8A==", "dev": true, "requires": { "esprima": "^4.0.1", @@ -9880,9 +10201,9 @@ } }, "eslint": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.0.0.tgz", - "integrity": "sha512-qY1cwdOxMONHJfGqw52UOpZDeqXy8xmD0u8CT6jIstil72jkhURC704W8CFyTPDPllz4z4lu0Ql1+07PG/XdIg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.2.0.tgz", + "integrity": "sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -9891,10 +10212,10 @@ "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", + "eslint-scope": "^5.1.0", "eslint-utils": "^2.0.0", - "eslint-visitor-keys": "^1.1.0", - "espree": "^7.0.0", + "eslint-visitor-keys": "^1.2.0", + "espree": "^7.1.0", "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", @@ -9940,9 +10261,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -9965,9 +10286,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -10102,32 +10423,33 @@ } }, "eslint-plugin-cypress": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.10.3.tgz", - "integrity": "sha512-CvFeoCquShfO8gHNIKA1VpUTz78WtknMebLemBd1lRbcmJNjwpqCqpQYUG/XVja8GjdX/e2TJXYa+EUBxehtUg==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.1.tgz", + "integrity": "sha512-MxMYoReSO5+IZMGgpBZHHSx64zYPSPTpXDwsgW7ChlJTF/sA+obqRbHplxD6sBStE+g4Mi0LCLkG4t9liu//mQ==", "dev": true, "requires": { "globals": "^11.12.0" } }, "eslint-plugin-import": { - "version": "2.20.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz", - "integrity": "sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==", + "version": "2.21.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.21.2.tgz", + "integrity": "sha512-FEmxeGI6yaz+SnEB6YgNHlQK1Bs2DKLM+YF+vuTk5H8J9CLbJLtlPvRFgZZ2+sXiKAlN5dpdlrWOjK8ZoZJpQA==", "dev": true, "requires": { - "array-includes": "^3.0.3", - "array.prototype.flat": "^1.2.1", + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", "contains-path": "^0.1.0", "debug": "^2.6.9", "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.1", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", "has": "^1.0.3", "minimatch": "^3.0.4", - "object.values": "^1.1.0", + "object.values": "^1.1.1", "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" }, "dependencies": { "doctrine": { @@ -10143,18 +10465,18 @@ } }, "eslint-plugin-jest": { - "version": "23.13.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.13.1.tgz", - "integrity": "sha512-TRLJH6M6EDvGocD98a7yVThrAOCK9WJfo9phuUb0MJptcrOYZeCKzC9aOzZCD93sxXCsiJVZywaTHdI/mAi0FQ==", + "version": "23.13.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.13.2.tgz", + "integrity": "sha512-qZit+moTXTyZFNDqSIR88/L3rdBlTU7CuW6XmyErD2FfHEkdoLgThkRbiQjzgYnX6rfgLx3Ci4eJmF4Ui5v1Cw==", "dev": true, "requires": { "@typescript-eslint/experimental-utils": "^2.5.0" } }, "eslint-plugin-jsdoc": { - "version": "25.4.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-25.4.2.tgz", - "integrity": "sha512-IFZnxBBt2fGYZ9yaLt+KP/jHa6u8LQPwH9QzRlhbU+WKBq7ou6XTXoxG0EZVn9ohcbJ0sM8X70iRRX/J3Wu37w==", + "version": "25.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-25.4.3.tgz", + "integrity": "sha512-5FyIKcNoM+AMBf1sTHvs2DVyij2x6eCYO1hxOUWFLBZV6H5AIdXPT3fVc9H6hrxOYe9SRF+5SjY1hqimV3Q+gQ==", "dev": true, "requires": { "comment-parser": "^0.7.4", @@ -10215,9 +10537,9 @@ } }, "eslint-plugin-prettier": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz", - "integrity": "sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz", + "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==", "dev": true, "requires": { "prettier-linter-helpers": "^1.0.0" @@ -10479,9 +10801,9 @@ }, "dependencies": { "globals": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.0.0.tgz", - "integrity": "sha512-TdLO3ykvHpNsJZiMr+RfkwoMI+XdDaFbH87w/UZGrSzNvZ5cUU6W50oPKSDZgb6eoMWZ6Pltf5Y3oQBpKLUSkA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.1.0.tgz", + "integrity": "sha512-4N8AdK8YMcr4nLOUsCP62jhMVAaJVdrEevrmuqHQ/TTXCXVL8ywhd/whKrufcp1zGtKBqw4DHcvsokQ60khOJA==", "dev": true, "requires": { "type-fest": "^0.8.1" @@ -10490,9 +10812,9 @@ } }, "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -10500,18 +10822,18 @@ } }, "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" } }, "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz", + "integrity": "sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==", "dev": true }, "esniff": { @@ -10525,14 +10847,14 @@ } }, "espree": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.0.0.tgz", - "integrity": "sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz", + "integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==", "dev": true, "requires": { - "acorn": "^7.1.1", + "acorn": "^7.2.0", "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^1.2.0" } }, "esprima": { @@ -11023,9 +11345,9 @@ } }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-diff": { @@ -11035,9 +11357,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", - "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -11121,9 +11443,9 @@ "dev": true }, "fast-xml-parser": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.17.1.tgz", - "integrity": "sha512-jZ0EVn1iBuZtx/sbQnfvhSaaUltz+0+yfR+6QRyzrlt5yMiU+8ZfGj9i3/hoXJxm+aFri7dycBWbncox7frCAQ==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.17.4.tgz", + "integrity": "sha512-qudnQuyYBgnvzf5Lj/yxMcf4L9NcVWihXJg7CiU1L+oUCq8MUnFEfH2/nXR/W5uq+yvUN1h7z6s7vs2v1WkL1A==", "dev": true }, "fastparse": { @@ -11196,25 +11518,6 @@ "integrity": "sha512-r70c72ln2YHzQINNfxDp02hAhbGkt1HffZ+Du8oetWDLjDtFja/Lm10lUaSh9e+wD+7VDvPee0b0C9SAy8pWZg==", "dev": true }, - "file-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/file-match/-/file-match-1.0.2.tgz", - "integrity": "sha1-ycrSZdLIrfOoFHWw30dYWQafrvc=", - "dev": true, - "requires": { - "utils-extend": "^1.0.6" - } - }, - "file-system": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/file-system/-/file-system-2.2.2.tgz", - "integrity": "sha1-fWWDPjojR9zZVqgTxncVPtPt2Yc=", - "dev": true, - "requires": { - "file-match": "^1.0.1", - "utils-extend": "^1.0.4" - } - }, "file-type": { "version": "12.4.2", "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", @@ -11624,9 +11927,9 @@ }, "dependencies": { "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, "type": { @@ -11983,9 +12286,9 @@ }, "dependencies": { "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, "slash": { @@ -12003,13 +12306,13 @@ "dev": true }, "globule": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", - "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "dev": true, "requires": { "glob": "~7.1.1", - "lodash": "~4.17.12", + "lodash": "~4.17.10", "minimatch": "~3.0.2" } }, @@ -12023,10 +12326,7 @@ }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } }, @@ -12369,9 +12669,9 @@ } }, "honeybadger-js": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/honeybadger-js/-/honeybadger-js-2.2.1.tgz", - "integrity": "sha512-KF1azMMFLm9km019DEORB5PaWGw5SylE8WTxXoG96655QFHXA4ToM5t+8/Po9TKZ5YhrULTVow6ly1h2aQ8qpg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/honeybadger-js/-/honeybadger-js-2.2.2.tgz", + "integrity": "sha512-dRdTngkp5oqZ7076DDDMle/XHvMuPDXrh35tFkAGJIUabWMj71IODRm9ggLenDF2XxfB6FjrK4Ry5YixZTM4Ug==", "dev": true }, "hoopy": { @@ -12746,9 +13046,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -13114,9 +13414,9 @@ "dev": true }, "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.2.0.tgz", + "integrity": "sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -13424,9 +13724,9 @@ "dev": true }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", "dev": true }, "is-ci": { @@ -13854,12 +14154,12 @@ "dev": true }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", "dev": true, "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "is-regexp": { @@ -14143,9 +14443,9 @@ } }, "jake": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.6.1.tgz", - "integrity": "sha512-pHUK3+V0BjOb1XSi95rbBksrMdIqLVC9bJqDnshVyleYsET3H0XAq+3VB2E3notcYvv4wRdRHn13p7vobG+wfQ==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", "dev": true, "requires": { "async": "0.9.x", @@ -14184,9 +14484,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -14258,9 +14558,9 @@ }, "dependencies": { "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -14269,9 +14569,9 @@ } }, "execa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.1.tgz", - "integrity": "sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz", + "integrity": "sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -14393,9 +14693,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -14491,9 +14791,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -14565,9 +14865,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -14758,9 +15058,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -14832,9 +15132,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -14909,9 +15209,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15038,9 +15338,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15226,9 +15526,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15312,9 +15612,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15408,9 +15708,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15496,9 +15796,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15583,9 +15883,9 @@ "dev": true }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15658,9 +15958,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -15763,9 +16063,9 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -16001,6 +16301,15 @@ "indento": "^1.1.7" } }, + "json2yaml": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/json2yaml/-/json2yaml-1.1.0.tgz", + "integrity": "sha1-VBTZB/mBZYa4DFE+wuOusquBmmw=", + "dev": true, + "requires": { + "remedial": "1.x" + } + }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", @@ -16011,10 +16320,7 @@ }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } }, @@ -16088,19 +16394,19 @@ } }, "jsx-ast-utils": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", - "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", + "integrity": "sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==", "dev": true, "requires": { - "array-includes": "^3.0.3", + "array-includes": "^3.1.1", "object.assign": "^4.1.0" } }, "jszip": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.4.0.tgz", - "integrity": "sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", + "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", "dev": true, "requires": { "lie": "~3.3.0", @@ -16219,9 +16525,9 @@ "dev": true }, "koa": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.12.0.tgz", - "integrity": "sha512-WlUBj6PXoVhjI5ljMmlyK+eqkbVFW5XQu8twz6bd4WM2E67IwKgPMu5wIFXGxAsZT7sW5xAB54KhY8WAEkLPug==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.12.1.tgz", + "integrity": "sha512-NuYVKjnBxeEe19VljPO9yNcaKKVrMagcax3jjzZtOlxRY2nThWKQqgnI3Pr1OG7mFtvySoDRixoUWZIt6R9C3A==", "dev": true, "requires": { "accepts": "^1.3.5", @@ -16299,46 +16605,6 @@ "passthrough-counter": "^1.0.0" } }, - "koa-router": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-7.4.0.tgz", - "integrity": "sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "http-errors": "^1.3.1", - "koa-compose": "^3.0.0", - "methods": "^1.0.1", - "path-to-regexp": "^1.1.1", - "urijs": "^1.19.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", - "dev": true, - "requires": { - "any-promise": "^1.1.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "kuler": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", @@ -16359,8 +16625,7 @@ "dependencies": { "bl": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz", - "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==", + "bundled": true, "dev": true, "requires": { "readable-stream": "^3.0.1" @@ -16368,14 +16633,12 @@ }, "chownr": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "bundled": true, "dev": true }, "end-of-stream": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "bundled": true, "dev": true, "requires": { "once": "^1.4.0" @@ -16383,42 +16646,34 @@ }, "fs-constants": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "bundled": true, "dev": true }, "inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "bundled": true, "dev": true }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "dev": true, + "version": "^1.2.5" }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "bundled": true, "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" }, "dependencies": { "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "version": "^1.2.5" } } }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "bundled": true, "dev": true, "requires": { "wrappy": "1" @@ -16426,8 +16681,7 @@ }, "pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "bundled": true, "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -16436,8 +16690,7 @@ }, "readable-stream": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "bundled": true, "dev": true, "requires": { "inherits": "^2.0.3", @@ -16447,14 +16700,12 @@ }, "safe-buffer": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "bundled": true, "dev": true }, "string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "bundled": true, "dev": true, "requires": { "safe-buffer": "~5.2.0" @@ -16462,8 +16713,7 @@ }, "tar-fs": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", - "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", + "bundled": true, "dev": true, "requires": { "chownr": "^1.1.1", @@ -16474,8 +16724,7 @@ }, "tar-stream": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz", - "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==", + "bundled": true, "dev": true, "requires": { "bl": "^3.0.0", @@ -16487,14 +16736,12 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "bundled": true, "dev": true }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "bundled": true, "dev": true } } @@ -16646,18 +16893,20 @@ } }, "lint-staged": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.4.tgz", - "integrity": "sha512-doTMGKXQAT34c3S3gwDrTnXmCZp/z1/92D8suPqqh755sKPT18ew1NoPNHxJdrvv1D4WrJ7CEnx79Ns3EdEFbg==", + "version": "10.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.10.tgz", + "integrity": "sha512-dgelFaNH6puUGAcU+OVMgbfpKSerNYsPSn6+nlbRDjovL0KigpsVpCu0PFZG6BJxX8gnHJqaZlR9krZamQsb0w==", "dev": true, "requires": { "chalk": "^4.0.0", + "cli-truncate": "2.1.0", "commander": "^5.1.0", "cosmiconfig": "^6.0.0", "debug": "^4.1.1", "dedent": "^0.7.0", + "enquirer": "^2.3.5", "execa": "^4.0.1", - "listr2": "^2.0.2", + "listr2": "^2.1.0", "log-symbols": "^4.0.0", "micromatch": "^4.0.2", "normalize-path": "^3.0.0", @@ -16666,6 +16915,12 @@ "stringify-object": "^3.3.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -16676,6 +16931,12 @@ "color-convert": "^2.0.1" } }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -16686,15 +16947,25 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -16717,9 +16988,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -16737,9 +17008,9 @@ } }, "execa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.1.tgz", - "integrity": "sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz", + "integrity": "sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -16777,6 +17048,12 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -16859,6 +17136,37 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -17006,25 +17314,19 @@ } }, "listr2": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.0.4.tgz", - "integrity": "sha512-oJaAcplPsa72rKW0eg4P4LbEJjhH+UO2I8uqR/I2wzHrVg16ohSfUy0SlcHS21zfYXxtsUpL8YXGHjyfWMR0cg==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.1.7.tgz", + "integrity": "sha512-XCC1sWLkBFFIMIRwG/LedgHUzN2XLEo02ZqXn6fwuP0GlXGE5BCuL6EAbQFb4vZB+++YEonzEXDPWQe+jCoF6Q==", "dev": true, "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", "chalk": "^4.0.0", - "cli-cursor": "^3.1.0", "cli-truncate": "^2.1.0", - "elegant-spinner": "^2.0.0", - "enquirer": "^2.3.5", "figures": "^3.2.0", "indent-string": "^4.0.0", "log-update": "^4.0.0", "p-map": "^4.0.0", - "pad": "^3.2.0", "rxjs": "^6.5.5", - "through": "^2.3.8", - "uuid": "^7.0.2" + "through": "^2.3.8" }, "dependencies": { "ansi-escapes": { @@ -17059,9 +17361,9 @@ "dev": true }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -17102,12 +17404,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "elegant-spinner": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-2.0.0.tgz", - "integrity": "sha512-5YRYHhvhYzV/FC4AiMdeSIg3jAYGq9xFvbhZMpPlJoBsfYgrw2DSCYeXfat6tYBu45PWiyRr3+flaCPPmviPaA==", - "dev": true - }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -17240,12 +17536,6 @@ "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", "dev": true }, - "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true - }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -17302,14 +17592,11 @@ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } } @@ -17618,6 +17905,12 @@ } } }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -17710,12 +18003,12 @@ "dev": true }, "madge": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/madge/-/madge-3.9.0.tgz", - "integrity": "sha512-ABR2ZTFga+TPLzlu2u46lcv8WwOzoI6VMTMqe3DyaY0igzyGpXiOIH1vAtziSKC4UHuyTpwkfhjWN5qOYR/5OA==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/madge/-/madge-3.9.2.tgz", + "integrity": "sha512-6ZvyKinAOOzcRpvpm1iyOuds+LvWIq3o3GmUYAHMJdIpDAgVY3mphxVzeWNo3agIOv0X0T/zbLycXQm9Rn19nA==", "dev": true, "requires": { - "chalk": "^4.0.0", + "chalk": "^4.1.0", "commander": "^5.1.0", "commondir": "^1.0.1", "debug": "^4.0.1", @@ -17733,8 +18026,10 @@ "ora": "^4.0.4", "pify": "^5.0.0", "pluralize": "^8.0.0", + "precinct": "^6.3.1", "pretty-ms": "^7.0.0", "rc": "^1.2.7", + "typescript": "^3.9.5", "walkdir": "^0.4.1" }, "dependencies": { @@ -17749,9 +18044,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -17895,9 +18190,9 @@ }, "dependencies": { "entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.2.tgz", - "integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", "dev": true } } @@ -17944,12 +18239,6 @@ "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", "dev": true }, - "md5-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-4.0.0.tgz", - "integrity": "sha512-UC0qFwyAjn4YdPpKaDNw6gNxRf7Mcx7jC1UGCY4boCzgvU2Aoc1mOGzTtrjjLKhM5ivsnhoKpQVxKPp+1j1qwg==", - "dev": true - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -18061,7 +18350,7 @@ "decamelize": "^1.1.2", "loud-rejection": "^1.0.0", "map-obj": "^1.0.1", - "minimist": "^1.1.3", + "minimist": "^1.2.5", "normalize-package-data": "^2.3.4", "object-assign": "^4.0.1", "read-pkg-up": "^1.0.1", @@ -18093,10 +18382,7 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" }, "path-exists": { "version": "2.1.0", @@ -18184,9 +18470,9 @@ "dev": true }, "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "methods": { @@ -18260,9 +18546,9 @@ "dev": true }, "min-indent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz", - "integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true }, "minimalistic-assert": { @@ -18287,10 +18573,8 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "dev": true, + "version": "^1.2.5" }, "minimist-options": { "version": "4.1.0", @@ -18374,10 +18658,7 @@ }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } }, @@ -18481,18 +18762,6 @@ "isarray": "^1.0.0" } }, - "multiparty": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.1.tgz", - "integrity": "sha512-AvESCnNoQlZiOfP9R4mxN8M9csy2L16EIbWIkt3l4FuGti9kXBS8QVzlfyg4HEnarJhrzZilgNFlZtqmoiAIIA==", - "dev": true, - "requires": { - "fd-slicer": "1.1.0", - "http-errors": "~1.7.0", - "safe-buffer": "5.1.2", - "uid-safe": "2.1.5" - } - }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -18630,9 +18899,9 @@ } }, "node-addon-api": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz", - "integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true }, "node-dir": { @@ -18776,9 +19045,9 @@ "dev": true }, "node-notifier": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.0.tgz", - "integrity": "sha512-y8ThJESxsHcak81PGpzWwQKxzk+5YtP3IxR8AYdpXQ1IB6FmcVzFdZXrkPin49F/DKUCfeeiziB8ptY9npzGuA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.1.tgz", + "integrity": "sha512-VkzhierE7DBmQEElhTGJIoiZa1oqRijOtgOlsXg32KrJRXsPy0NXFBqWGW/wTswnJlDCs5viRYaqWguqzsKcmg==", "dev": true, "optional": true, "requires": { @@ -18807,9 +19076,9 @@ } }, "node-releases": { - "version": "1.1.56", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.56.tgz", - "integrity": "sha512-EVo605FhWLygH8a64TjgpjyHYOihkxECwX1bHHr8tETJKWEiWS2YJjPbvsX2jFjnjTNEgBCmk9mLjKG1Mf11cw==", + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==", "dev": true }, "node-sass": { @@ -19240,14 +19509,13 @@ } }, "object.entries": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", - "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", "dev": true, "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.17.5", "has": "^1.0.3" } }, @@ -19347,9 +19615,9 @@ "dev": true }, "opencollective-postinstall": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", - "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", "dev": true }, "opn": { @@ -20003,15 +20271,6 @@ } } }, - "pad": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pad/-/pad-3.2.0.tgz", - "integrity": "sha512-2u0TrjcGbOjBTJpyewEl4hBO3OeX5wWue7eIFPzQTg6wFSvoaHcBTTUY5m+n0hd04gmTCPuY0kCpVIVuw5etwg==", - "dev": true, - "requires": { - "wcwidth": "^1.0.1" - } - }, "pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", @@ -20140,14 +20399,11 @@ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } }, @@ -20314,12 +20570,11 @@ } }, "parcel-plugin-static-files-copy": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/parcel-plugin-static-files-copy/-/parcel-plugin-static-files-copy-2.3.1.tgz", - "integrity": "sha512-yqB1bhSK+hbfxSjc1y/gBc+Fm6bedNrofx75wgnI0sP+6oEBqjyN51tlJVLu6pZhBLi11ZFwAM2XubCUh2G0+A==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/parcel-plugin-static-files-copy/-/parcel-plugin-static-files-copy-2.4.3.tgz", + "integrity": "sha512-tUZn54XsZIZ9hhhNQkyaWaHLvjOj2wra8pp4yPjp1b5p1frQ+YEwG6eaNtaQVyA+UvORsZg+Wni7n9gIOBE8eA==", "dev": true, "requires": { - "file-system": "2.2.2", "minimatch": "3.0.4", "path": "0.12.7" } @@ -20554,9 +20809,9 @@ } }, "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -20567,9 +20822,9 @@ } }, "pdf-lib": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.6.0.tgz", - "integrity": "sha512-/015/tCCpv/mdcGlnwL2pHfh6/0fdtjrcg1b/sv/bkFEFTEn7ptvfkNoTXpum4B3WsarMwllOgvMrOVUMDuI7g==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.7.0.tgz", + "integrity": "sha512-JauBSWwR5hCsdqxCsm1vJUJ4R1MSikzxWzi+E4zFFMU0zZHX71u18oyW0ZaELXJW13L11I/2LKrUJEhTISUzzg==", "dev": true, "requires": { "@pdf-lib/standard-fonts": "^0.0.4", @@ -20811,9 +21066,9 @@ "dev": true }, "postcss": { - "version": "7.0.30", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.30.tgz", - "integrity": "sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ==", + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -21573,22 +21828,22 @@ "dev": true }, "precinct": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/precinct/-/precinct-6.2.0.tgz", - "integrity": "sha512-BCAmnOxZzobF3H1/h/gq70pEyvX/BVLWCrzi8beFD22dqu5Z14qOghNUsI24Wg8oaTsGFcIjOGtFX5L9ttmjVg==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/precinct/-/precinct-6.3.1.tgz", + "integrity": "sha512-JAwyLCgTylWminoD7V0VJwMElWmwrVSR6r9HaPWCoswkB4iFzX7aNtO7VBfAVPy+NhmjKb8IF8UmlWJXzUkOIQ==", "dev": true, "requires": { - "commander": "^2.19.0", + "commander": "^2.20.3", "debug": "^4.1.1", "detective-amd": "^3.0.0", "detective-cjs": "^3.1.1", - "detective-es6": "^2.0.0", + "detective-es6": "^2.1.0", "detective-less": "^1.0.2", - "detective-postcss": "^3.0.0", - "detective-sass": "^3.0.0", - "detective-scss": "^2.0.0", + "detective-postcss": "^3.0.1", + "detective-sass": "^3.0.1", + "detective-scss": "^2.0.1", "detective-stylus": "^1.0.0", - "detective-typescript": "^5.1.1", + "detective-typescript": "^5.8.0", "module-definition": "^3.3.0", "node-source-walk": "^4.2.0" }, @@ -21811,6 +22066,35 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, + "protobufjs": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.9.0.tgz", + "integrity": "sha512-LlGVfEWDXoI/STstRDdZZKb/qusoAWUnmLg9R8OLSO473mBLWHowx8clbX5/+mKDEI+v7GzjoK9tRPZMMcoTrg==", + "dev": true, + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "13.13.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz", + "integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==", + "dev": true + } + } + }, "protocolify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/protocolify/-/protocolify-2.0.0.tgz", @@ -22072,9 +22356,9 @@ } }, "mime": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz", - "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", "dev": true }, "ms": { @@ -22120,9 +22404,9 @@ } }, "mime": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz", - "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", "dev": true }, "ms": { @@ -22295,9 +22579,9 @@ "dev": true }, "query-string": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.12.1.tgz", - "integrity": "sha512-OHj+zzfRMyj3rmo/6G8a5Ifvw3AleL/EbcHMD27YA31Q+cO5lfmQxECkImuNVjcskLcvBRVHNAB3w6udMs1eAA==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz", + "integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==", "dev": true, "requires": { "decode-uri-component": "^0.2.0", @@ -22391,15 +22675,12 @@ "dev": true, "requires": { "buffer-equal": "0.0.1", - "minimist": "^1.1.3", + "minimist": "^1.2.5", "through2": "^2.0.0" }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } }, @@ -22434,12 +22715,6 @@ "ret": "~0.1.10" } }, - "random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", - "dev": true - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -22485,15 +22760,12 @@ "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", - "minimist": "^1.2.0", + "minimist": "^1.2.5", "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" }, "strip-json-comments": { "version": "2.0.1", @@ -22580,9 +22852,9 @@ } }, "react-responsive": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.0.3.tgz", - "integrity": "sha512-F9VXyLao7O8XHXbLjQbIr4+mC6Zr0RDTwNjd7ixTmYEAyKyNanBkLkFchNaMZgszoSK6PgSs/3m/QDWw33/gpg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.1.0.tgz", + "integrity": "sha512-U8Nv2/ZWACIw/fAE9XNPbc2Xo33X5q1bcCASc2SufvJ9ifB+o/rokfogfznSVcvS22hN1rafGi0uZD6GiVFEHw==", "dev": true, "requires": { "hyphenate-style-name": "^1.0.0", @@ -22724,9 +22996,9 @@ "dev": true }, "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", "dev": true }, "regenerate-unicode-properties": { @@ -22819,9 +23091,9 @@ } }, "regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", "dev": true }, "regjsparser": { @@ -22877,9 +23149,9 @@ } }, "remark-stringify": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.0.0.tgz", - "integrity": "sha512-cABVYVloFH+2ZI5bdqzoOmemcz/ZuhQSH6W6ZNYnLojAUUn3xtX7u+6BpnYp35qHoGr2NFBsERV14t4vCIeW8w==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.0.tgz", + "integrity": "sha512-FSPZv1ds76oAZjurhhuV5qXSUSoz6QRPuwYK38S41sLHwg4oB7ejnmZshj7qwjgYLf93kdz6BOX9j5aidNE7rA==", "dev": true, "requires": { "ccount": "^1.0.0", @@ -22898,6 +23170,12 @@ "xtend": "^4.0.1" } }, + "remedial": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", + "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", + "dev": true + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -23296,29 +23574,28 @@ } }, "s3rver": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/s3rver/-/s3rver-3.5.0.tgz", - "integrity": "sha512-hZdrWvAzPr3Np8TJTEqDog41qUqdZaPDW7i5EhgQ2QfsQIwGxw56zQCXmQ+vVD2ks23vpn4u400kWVnWdGft0w==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/s3rver/-/s3rver-3.6.1.tgz", + "integrity": "sha512-8PI1yIUJ6WQbDYjidZo/EOknMRxos3uukM1a70VQE2lqzH9A8FUTOhlUGaRiAU/K0teROou1mesUsfrYwpwkCw==", "dev": true, "requires": { - "commander": "^3.0.0", + "@koa/router": "^9.0.0", + "busboy": "^0.3.1", + "commander": "^5.0.0", "fast-xml-parser": "^3.12.19", "fs-extra": "^8.0.0", "he": "^1.2.0", "koa": "^2.7.0", "koa-logger": "^3.2.0", - "koa-router": "^7.4.0", "lodash": "^4.17.5", - "md5-file": "^4.0.0", - "multiparty": "^4.2.1", - "statuses": "^1.5.0", + "statuses": "^2.0.0", "winston": "^3.0.0" }, "dependencies": { "commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true }, "readable-stream": { @@ -23332,6 +23609,12 @@ "util-deprecate": "^1.0.1" } }, + "statuses": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.0.tgz", + "integrity": "sha512-w9jNUUQdpuVoYqXxnyOakhckBbOxRaoYqJscyIBYCS5ixyCnO7nQn7zBZvP9zf5QOPZcz2DLUpE3KsNPbJBOFA==", + "dev": true + }, "winston": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", @@ -23385,15 +23668,12 @@ "execa": "^1.0.0", "fb-watchman": "^2.0.0", "micromatch": "^3.1.4", - "minimist": "^1.1.1", + "minimist": "^1.2.5", "walker": "~1.0.5" }, "dependencies": { "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" } } }, @@ -23407,9 +23687,9 @@ } }, "sass": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.5.tgz", - "integrity": "sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q==", + "version": "1.26.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.8.tgz", + "integrity": "sha512-yvtzyrKLGiXQu7H12ekXqsfoGT/aTKeMDyVzCB675k1HYuaj0py63i8Uf4SI9CHXj6apDhpfwbUr3gGOjdpu2Q==", "dev": true, "requires": { "chokidar": ">=2.0.0 <4.0.0" @@ -23713,10 +23993,13 @@ } }, "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } }, "serialize-to-js": { "version": "3.1.1", @@ -23797,9 +24080,9 @@ }, "dependencies": { "@serverless/components": { - "version": "2.30.11", - "resolved": "https://registry.npmjs.org/@serverless/components/-/components-2.30.11.tgz", - "integrity": "sha512-hji/a7li5prjGYf958kxXfwmiJZlVhrIR7WrdyV84FYUdqnwXc15ReJaL5LDl0TU0AeXnye7mpcHJ/Dlilzc8w==", + "version": "2.30.15", + "resolved": "https://registry.npmjs.org/@serverless/components/-/components-2.30.15.tgz", + "integrity": "sha512-1K8L1DV1pUEYeBtUyJZfCSpfzBuuvCEPwAuu/D89g4Lxlp5DcrnkXUItKFf1sprTR/20q3tS9iKdcgPsU5QFCA==", "dev": true, "requires": { "@serverless/inquirer": "^1.1.0", @@ -23859,10 +24142,7 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" }, "semver": { "version": "7.3.2", @@ -24129,9 +24409,9 @@ } }, "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, "inquirer": { @@ -24373,9 +24653,9 @@ } }, "serverless-domain-manager": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/serverless-domain-manager/-/serverless-domain-manager-4.1.0.tgz", - "integrity": "sha512-8RK4qJ0pCoCSzj4eS32z49tKwymntO/dx7xQ3m2lYobaGtNNC0ESSdBAl1ILadyWJ0jzzZ1HysdDcZl+M+LMFQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/serverless-domain-manager/-/serverless-domain-manager-4.1.1.tgz", + "integrity": "sha512-9cQC+aj7FD82ca7SC1fWLKZzyDyRufj+ez0SC89VeNQ43UfugstSSCqbJ6nOWSgSeLQdEKZzukY61vcOF957LA==", "dev": true, "requires": { "aws-sdk": "^2.490.0", @@ -24695,23 +24975,14 @@ } }, "serverless-plugin-aws-alerts": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/serverless-plugin-aws-alerts/-/serverless-plugin-aws-alerts-1.4.0.tgz", - "integrity": "sha512-4ChkFVVi3X4NeqgdxAhaVha8LnXrH8kadMm5iV1TMyOr/zNdjo9NoRCLnHkspODO4f2uqwlEiMuh9yUrT6s7PA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/serverless-plugin-aws-alerts/-/serverless-plugin-aws-alerts-1.5.2.tgz", + "integrity": "sha512-ZQ4adV2r9pSX0ARcvLO2bXr0Ql359lYW8w/a0FmmtynjwhYrEw1jhlYQhToZQ/ruS4OrOsRGEvyuf58aTZhhkQ==", "dev": true, "requires": { "lodash": "^4.17.10" } }, - "serverless-plugin-bind-deployment-id": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/serverless-plugin-bind-deployment-id/-/serverless-plugin-bind-deployment-id-1.2.0.tgz", - "integrity": "sha512-WYyrT8HXLmh44A3NP/i+q5h6dK1pkDka9OvhRIScuwj3yZ3nNmZ7m6qsnjJwpz6OMjoj7kPbcP9GYDU9kQWDMQ==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, "serverless-plugin-git-variables": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/serverless-plugin-git-variables/-/serverless-plugin-git-variables-4.0.0.tgz", @@ -24781,9 +25052,9 @@ }, "dependencies": { "fs-extra": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz", - "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", "dev": true, "requires": { "at-least-node": "^1.0.0", @@ -25321,9 +25592,9 @@ "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -25510,9 +25781,9 @@ "dev": true }, "static-eval": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.5.tgz", - "integrity": "sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz", + "integrity": "sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==", "dev": true, "requires": { "escodegen": "^1.11.1" @@ -25743,6 +26014,33 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "stream.finished": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stream.finished/-/stream.finished-1.2.0.tgz", + "integrity": "sha512-xSp45f/glqd035qAtFUxAGvhotjY/EfqDNV+rQW8o7ffligiOjPaguTEvRzeQAhiQMCdkPEBrp5++S/rQyavWQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "stream.pipeline-shim": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stream.pipeline-shim/-/stream.pipeline-shim-1.1.0.tgz", + "integrity": "sha512-pSi/SZZDbSA5l3YYjSmJadCoD74/qSe79r9ZVR21lD4bpf+khn5Umi6AlfJrD8I0KQfGSqm/7Yp48dmithM+Vw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1", + "stream.finished": "^1.2.0" + } + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "dev": true + }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -25854,28 +26152,6 @@ "es-abstract": "^1.17.5" } }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } - }, "string.prototype.trimstart": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", @@ -26018,14 +26294,14 @@ } }, "stylelint": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.5.0.tgz", - "integrity": "sha512-+Jy7ieKAWKTf2tmcAE7jgScxH39Urb87i0bjK/enScFaGWWaFn4kAPwepGOSk2b7CLUDVt/O6kwA0x0p/V7moQ==", + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.6.0.tgz", + "integrity": "sha512-55gG2pNjVr183JJM/tlr3KAua6vTVX7Ho/lgKKuCIWszTZ1gmrXjX4Wok53SI8wRYFPbwKAcJGULQ77OJxTcNw==", "dev": true, "requires": { "@stylelint/postcss-css-in-js": "^0.37.1", "@stylelint/postcss-markdown": "^0.36.1", - "autoprefixer": "^9.7.6", + "autoprefixer": "^9.8.0", "balanced-match": "^1.0.0", "chalk": "^4.0.0", "cosmiconfig": "^6.0.0", @@ -26034,10 +26310,10 @@ "file-entry-cache": "^5.0.1", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.0", + "globby": "^11.0.1", "globjoin": "^0.1.4", "html-tags": "^3.1.0", - "ignore": "^5.1.4", + "ignore": "^5.1.8", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "known-css-properties": "^0.19.0", @@ -26048,7 +26324,7 @@ "meow": "^7.0.1", "micromatch": "^4.0.2", "normalize-selector": "^0.2.0", - "postcss": "^7.0.30", + "postcss": "^7.0.32", "postcss-html": "^0.36.0", "postcss-less": "^3.1.4", "postcss-media-query-parser": "^0.2.3", @@ -26056,7 +26332,7 @@ "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^4.0.2", "postcss-sass": "^0.4.4", - "postcss-scss": "^2.0.0", + "postcss-scss": "^2.1.1", "postcss-selector-parser": "^6.0.2", "postcss-syntax": "^0.36.2", "postcss-value-parser": "^4.1.0", @@ -26069,7 +26345,7 @@ "sugarss": "^2.0.0", "svg-tags": "^1.0.0", "table": "^5.4.6", - "v8-compile-cache": "^2.1.0", + "v8-compile-cache": "^2.1.1", "write-file-atomic": "^3.0.3" }, "dependencies": { @@ -26124,9 +26400,9 @@ } }, "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -26183,9 +26459,9 @@ "dev": true }, "globby": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.0.tgz", - "integrity": "sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -26209,9 +26485,9 @@ "dev": true }, "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, "import-lazy": { @@ -26656,9 +26932,9 @@ } }, "css-what": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", - "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz", + "integrity": "sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==", "dev": true }, "domutils": { @@ -26680,23 +26956,23 @@ } }, "swagger-cli": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/swagger-cli/-/swagger-cli-4.0.2.tgz", - "integrity": "sha512-XzKVHMDGlmiK+dgTxRSX7SSbG5KDsU4RS9eX3VSBw6ZPfW1aWYAbo2FuhArnsFMiz4/0KXJ8D/Lkii7xeSXviw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/swagger-cli/-/swagger-cli-4.0.3.tgz", + "integrity": "sha512-FRv5daUKnWdTgo5G3yBNkSRIfpcnc4wzw06cX/aBco9NOHeEJQMpbbq3YjHXXqUbXI7vZzCQDnoQVHgYCGQx7A==", "dev": true, "requires": { - "@apidevtools/swagger-cli": "4.0.2" + "@apidevtools/swagger-cli": "4.0.3" }, "dependencies": { "@apidevtools/swagger-cli": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-cli/-/swagger-cli-4.0.2.tgz", - "integrity": "sha512-f2u+J7QlPySpMlB/jG522/8lKFXxeYt4ygd4FXZlidGrZsTE4Dbc5Hor4zO8vvM5QjrACvpN3zDnKIpb8QVR1g==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-cli/-/swagger-cli-4.0.3.tgz", + "integrity": "sha512-HAs7WAZZVf6YvPlWFXSjhAE+x5TJS/EzoNqygEX9xBB509GEdvcLmahapql/1ng0iIEgj44tJU6kirCKMhDAzw==", "dev": true, "requires": { "@apidevtools/swagger-parser": "^9.0.1", - "chalk": "^3.0.0", - "js-yaml": "^3.13.1", + "chalk": "^4.0.0", + "js-yaml": "^3.14.0", "yargs": "^15.3.1" } }, @@ -26711,9 +26987,9 @@ } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -26830,7 +27106,7 @@ "debug": "^4.0.1", "es6-promisify": "^6.0.0", "inquirer": "^6.0.0", - "minimist": "^1.2.0", + "minimist": "^1.2.5", "mkdirp": "^0.5.1", "untildify": "^3.0.3" }, @@ -26896,10 +27172,7 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "^1.2.5" }, "ms": { "version": "2.1.2", @@ -27133,16 +27406,16 @@ } }, "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz", + "integrity": "sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA==", "dev": true, "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", + "serialize-javascript": "^3.1.0", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", @@ -27509,6 +27782,37 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", "dev": true }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.5", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "^1.2.5" + } + } + }, + "minimist": { + "version": "^1.2.5" + } + } + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -27604,9 +27908,9 @@ } }, "typescript": { - "version": "3.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz", - "integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==", + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz", + "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", "dev": true }, "uc.micro": { @@ -27670,15 +27974,6 @@ "dev": true, "optional": true }, - "uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "dev": true, - "requires": { - "random-bytes": "~1.0.0" - } - }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", @@ -27695,9 +27990,9 @@ } }, "unbzip2-stream": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.2.tgz", - "integrity": "sha512-pZMVAofMrrHX6Ik39hCk470kulCbmZ2SWfQLPmTWqfJV/oUm0gn1CblvHdUu4+54Je6Jq34x8kY6XjTy6dMkOg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, "requires": { "buffer": "^5.2.1", @@ -28335,9 +28630,9 @@ }, "dependencies": { "@types/node": { - "version": "13.13.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.8.tgz", - "integrity": "sha512-WJoiKALUF5exZo0G3T5coauJR2Tmc6rdE9/kgppZVnV6rlUB2dl3gTu2GTNBKhKF6SZ/WFfpEUIGNC/0qvdMWA==", + "version": "13.13.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz", + "integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==", "dev": true }, "typescript": { @@ -28395,12 +28690,6 @@ "object.getownpropertydescriptors": "^2.1.0" } }, - "utils-extend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/utils-extend/-/utils-extend-1.0.8.tgz", - "integrity": "sha1-zP17ZFQPjpDuIe7Fd2nQZRyril8=", - "dev": true - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -28414,9 +28703,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", "dev": true }, "v8-to-istanbul": { @@ -29189,16 +29478,6 @@ "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", "dev": true }, - "yamljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", - "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "glob": "^7.0.5" - } - }, "yargs": { "version": "15.3.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", @@ -29379,4 +29658,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index ea46232b25e..d0563a1d9a7 100644 --- a/package.json +++ b/package.json @@ -5,21 +5,21 @@ "license": "CC0-1.0", "repository": "https://github.com/ustaxcourt/ef-cms", "devDependencies": { - "@babel/cli": "^7.8.4", - "@babel/core": "^7.9.6", - "@babel/preset-env": "^7.9.6", - "@babel/preset-react": "^7.9.4", - "@babel/register": "^7.9.0", + "@babel/cli": "^7.10.1", + "@babel/core": "^7.10.2", + "@babel/preset-env": "^7.10.2", + "@babel/preset-react": "^7.10.0", + "@babel/register": "^7.10.1", "@cerebral/react": "4.2.1-1584683380023", "@fortawesome/fontawesome-svg-core": "^1.2.28", "@fortawesome/free-regular-svg-icons": "^5.13.0", "@fortawesome/free-solid-svg-icons": "^5.13.0", - "@fortawesome/react-fontawesome": "^0.1.9", + "@fortawesome/react-fontawesome": "^0.1.10", "@hapi/joi": "^17.1.0", "@hapi/joi-date": "^2.0.1", "archiver": "3.1.1", "autoprefixer": "^9.8.0", - "aws-sdk": "^2.681.0", + "aws-sdk": "^2.693.0", "aws-sdk-mock": "^5.0.0", "aws-xray-sdk": "^2.5.0", "axios": "^0.19.2", @@ -32,19 +32,19 @@ "classnames": "^2.2.6", "core-js": "^3.6.5", "csv-parse": "^4.10.1", - "cypress": "^4.6.0", + "cypress": "^4.8.0", "deep-freeze": "0.0.1", "diff-arrays-of-objects": "^1.1.5", "dynamodb-admin": "^4.0.0", "elasticsearch": "^16.6.0", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", - "eslint": "^7.0.0", + "eslint": "^7.1.0", "eslint-config-prettier": "^6.11.0", - "eslint-plugin-cypress": "^2.10.3", + "eslint-plugin-cypress": "^2.11.1", "eslint-plugin-import": "^2.20.2", - "eslint-plugin-jest": "^23.13.1", - "eslint-plugin-jsdoc": "^25.4.2", + "eslint-plugin-jest": "^23.13.2", + "eslint-plugin-jsdoc": "^25.4.3", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-promise": "^4.2.1", @@ -59,7 +59,7 @@ "faker": "^4.1.0", "form-data": "^3.0.0", "honeybadger": "^1.4.0", - "honeybadger-js": "^2.2.1", + "honeybadger-js": "^2.2.2", "http-aws-es": "^6.0.0", "http-proxy": "^1.18.1", "http-proxy-middleware": "^1.0.4", @@ -70,13 +70,14 @@ "is-reachable": "^4.0.0", "isomorphic-fetch": "^2.2.1", "jest": "^26.0.1", - "js-yaml": "^3.13.1", + "js-yaml": "^3.14.0", "jsdoc": "^3.6.4", "jsdom": "^16.2.2", "json2md": "^1.7.0", + "json2yaml": "^1.1.0", "jsonwebtoken": "^8.5.1", "jwk-to-pem": "^2.0.3", - "lint-staged": "^10.2.4", + "lint-staged": "^10.2.9", "lodash": "^4.17.15", "madge": "^3.9.0", "moment": "^2.25.3", @@ -87,9 +88,9 @@ "pa11y-ci": "^2.3.0", "parcel-bundler": "^1.12.4", "parcel-plugin-bundle-visualiser": "^1.2.0", - "parcel-plugin-static-files-copy": "^2.3.1", - "pdf-lib": "^1.6.0", - "pdfjs-dist": "^2.3.200", + "parcel-plugin-static-files-copy": "^2.4.1", + "pdf-lib": "^1.7.0", + "pdfjs-dist": "2.3.200", "pixelmatch": "^5.2.0", "pngjs": "^5.0.0", "prettier": "^2.0.4", @@ -106,7 +107,7 @@ "react-idle-timer": "^4.2.12", "react-number-format": "^4.4.1", "react-quill": "^2.0.0-beta.2", - "react-responsive": "^8.0.2", + "react-responsive": "^8.1.0", "react-select": "^3.1.0", "react-test-renderer": "^16.13.1", "readline": "^1.3.0", @@ -115,25 +116,24 @@ "s3-zip": "^3.1.3", "s3rver": "^3.5.0", "sanitize-filename": "^1.6.3", - "sass": "^1.26.5", + "sass": "^1.26.8", "serverless": "1.70.1", - "serverless-domain-manager": "^4.1.0", + "serverless-domain-manager": "^4.1.1", "serverless-jetpack": "^0.10.4", "serverless-latest-layer-version": "^2.1.0", "serverless-log-forwarding": "^1.4.0", "serverless-offline": "^5.12.1", "serverless-offline-dynamodb-streams": "^3.0.2", - "serverless-plugin-aws-alerts": "^1.3.1", - "serverless-plugin-bind-deployment-id": "^1.2.0", + "serverless-plugin-aws-alerts": "^1.5.1", "serverless-plugin-git-variables": "^4.0.0", "serverless-plugin-split-stacks": "^1.9.3", "serverless-plugin-tracing": "^2.0.0", "serverless-plugin-warmup": "^4.9.0", "serverless-prune-plugin": "^1.4.3", - "stylelint": "^13.4.1", + "stylelint": "^13.6.0", "stylelint-config-idiomatic-order": "^8.1.0", "stylelint-config-standard": "^20.0.0", - "swagger-cli": "^4.0.2", + "swagger-cli": "^4.0.3", "tmp": "^0.2.1", "umzug": "^2.3.0", "uswds": "^2.7.0", @@ -168,6 +168,7 @@ "build:api:case:parties": "npm run build:_api -- web-api/src/casePartiesHandlers.js", "build:api:cases": "npm run build:_api -- web-api/src/casesHandlers.js", "build:api:documents": "npm run build:_api -- web-api/src/documentsHandlers.js", + "build:api:messages": "npm run build:_api -- web-api/src/messagesHandlers.js", "build:api:migrate": "npm run build:_api -- web-api/src/migrateHandlers.js", "build:api:notifications": "npm run build:_api -- web-api/src/notificationHandlers.js", "build:api:practitioners": "npm run build:_api -- web-api/src/practitionersHandlers.js", @@ -187,9 +188,15 @@ "clean:public": "rm -rf dist-public/", "clean": "./reset-dependencies.sh CLEAN_ONLY", "cypress:open": "TEMP_DOCUMENTS_BUCKET_NAME=noop-temp-documents-local-us-east-1 DOCUMENTS_BUCKET_NAME=noop-documents-local-us-east-1 S3_ENDPOINT=http://localhost:9000 MASTER_DYNAMODB_ENDPOINT=http://localhost:8000 AWS_ACCESS_KEY_ID=S3RVER AWS_SECRET_ACCESS_KEY=S3RVER SLS_DEPLOYMENT_BUCKET=noop cypress open", + "cypress:public": "TEMP_DOCUMENTS_BUCKET_NAME=noop-temp-documents-local-us-east-1 DOCUMENTS_BUCKET_NAME=noop-documents-local-us-east-1 S3_ENDPOINT=http://localhost:9000 MASTER_DYNAMODB_ENDPOINT=http://localhost:8000 AWS_ACCESS_KEY_ID=S3RVER AWS_SECRET_ACCESS_KEY=S3RVER SLS_DEPLOYMENT_BUCKET=noop cypress run --spec cypress/integration/public/** --config-file cypress-public.json", "cypress:smoketests:open": "cypress open -C cypress-smoketests.json --env AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY,ENV=$ENV", "cypress:smoketests": "cypress run -C cypress-smoketests.json --env AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY,ENV=$ENV", - "cypress": "TEMP_DOCUMENTS_BUCKET_NAME=noop-temp-documents-local-us-east-1 DOCUMENTS_BUCKET_NAME=noop-documents-local-us-east-1 S3_ENDPOINT=http://localhost:9000 MASTER_DYNAMODB_ENDPOINT=http://localhost:8000 AWS_ACCESS_KEY_ID=S3RVER AWS_SECRET_ACCESS_KEY=S3RVER SLS_DEPLOYMENT_BUCKET=noop cypress run", + "cypress": "TEMP_DOCUMENTS_BUCKET_NAME=noop-temp-documents-local-us-east-1 DOCUMENTS_BUCKET_NAME=noop-documents-local-us-east-1 S3_ENDPOINT=http://localhost:9000 MASTER_DYNAMODB_ENDPOINT=http://localhost:8000 AWS_ACCESS_KEY_ID=S3RVER AWS_SECRET_ACCESS_KEY=S3RVER SLS_DEPLOYMENT_BUCKET=noop cypress run --spec \"cypress/integration/*\"", + "destroy:dev": "node env-destroy-utils/destroyEnvironment.js dev", + "destroy:exp1": "node env-destroy-utils/destroyEnvironment.js exp1", + "destroy:exp2": "node env-destroy-utils/destroyEnvironment.js exp2", + "destroy:prod": "node env-destroy-utils/destroyEnvironment.js prod", + "destroy:stg": "node env-destroy-utils/destroyEnvironment.js stg", "dynamo:admin": "DYNAMO_ENDPOINT=http://localhost:8000 dynamodb-admin", "dynamo:export": "AWS_ACCESS_KEY_ID=noop AWS_SECRET_ACCESS_KEY=noop SLS_DEPLOYMENT_BUCKET=noop node web-api/dynamo-export.js > ./web-api/storage/fixtures/efcms.json", "dynamo:import": "pushd ./web-api && node ./create-dynamo-tables.js && node ./seed-dynamo.js ./storage/fixtures/efcms.json && popd", @@ -199,9 +206,10 @@ "lint:fix": "run-p lint:css:fix lint:js:fix", "lint:js:fix:plain": "npm run lint:js:fix -- --rule 'prettier/prettier: 0'", "lint:js:fix": "npm run lint:js -- --fix", - "lint:js": "TIMING=1 eslint --ext .js,.jsx shared/src/ web-api/src/ web-client/src/ web-client/integration-tests/", + "lint:js": "TIMING=1 eslint --ext .js,.jsx shared/src/ web-api/storage/ web-api/src/ web-client/src/ web-client/integration-tests/", + "lint:serverless": "node ./validateServerless.js", "lint:swagger": "swagger-cli validate web-api/swagger.json", - "lint": "npx run-p lint:swagger lint:css lint:js", + "lint": "npx run-p lint:swagger lint:css lint:js lint:serverless", "loadtest:high": "./web-api/load-test-trial-session.sh dev us-east-1 150", "loadtest:medium": "./web-api/load-test-trial-session.sh dev us-east-1 125", "loadtest:practitioners": "./web-api/load-test-practitioners.sh dev us-east-1 200", @@ -241,17 +249,21 @@ "test:pa11y": "npm run test:pa11y:1 && npm run test:pa11y:2 && npm run test:pa11y:3", "test:pdf-output": "npm run build:assets && PDF_OUTPUT=true jest shared/src/business/utilities/documentGenerators.test.js", "test:pdf": "npm run build:assets && node ./shared/pdf-tests/pdfReportTester.js", - "test:public:integration": "npm run test:_client -- \"web-client/integration-tests-public/.*\\.test\\.js\"", + "test:public:integration": "npm run test:client:_integration \"web-client/integration-tests-public/.*\\.test\\.js\"", "test:shared": "npm run build:assets && jest --maxWorkers=50% --config shared/jest.config.js \"shared/.*\\.test\\.js\"", "test:watch": "jest --watch .*test\\.js$", "test": "echo 'Best run with start:api:ci and start:client:ci' && sleep 3 && CI=true npx run-s test:shared test:api test:client test:pa11y cypress lint", "watch": "nodemon -e yml --exec sls offline start" }, "staticFiles": { - "staticPath": "node_modules/pdfjs-dist/build", + "staticPath": [ + "node_modules/pdfjs-dist/build", + "node_modules/react-quill/dist", + "node_modules/pdf-lib/dist" + ], "watcherGlob": false }, "resolutions": { "minimist": "^1.2.5" } -} +} \ No newline at end of file diff --git a/reset-dependencies.sh b/reset-dependencies.sh index 24740cbe469..d7577d8f799 100755 --- a/reset-dependencies.sh +++ b/reset-dependencies.sh @@ -24,4 +24,4 @@ if [ -z "$CLEAN_ONLY" ]; then npm update npx npm-force-resolutions npm prune -fi \ No newline at end of file +fi diff --git a/run-local.sh b/run-local.sh index f90f0bb7c6b..d185f9b11ac 100755 --- a/run-local.sh +++ b/run-local.sh @@ -26,7 +26,7 @@ export SKIP_VIRUS_SCAN=true export AWS_ACCESS_KEY_ID=S3RVER export AWS_SECRET_ACCESS_KEY=S3RVER export SLS_DEPLOYMENT_BUCKET=S3RVER -export MASTER_DYNAMODB_ENDPOINT=http://localhost:8000 +export MASTER_DYNAMODB_ENDPOINT=http://localhost:8000 export S3_ENDPOINT=http://localhost:9000 export DOCUMENTS_BUCKET_NAME=noop-documents-local-us-east-1 export TEMP_DOCUMENTS_BUCKET_NAME=noop-temp-documents-local-us-east-1 @@ -68,6 +68,7 @@ set -- \ --stage local \ --stageColor "blue" \ --dynamo_stream_arn "arn:aws:dynamodb:ddblocal:000000000000:table/efcms-local/stream/*" \ + --circleHoneybadgerApiKey noop \ --elasticsearch_endpoint "http://localhost:9200" echo "starting public api service" @@ -106,6 +107,8 @@ echo "starting reports service" npx sls offline start "$@" --config web-api/serverless-reports.yml & echo "starting practitioners service" npx sls offline start "$@" --config web-api/serverless-practitioners.yml & +echo "starting messages service" +npx sls offline start "$@" --config web-api/serverless-messages.yml & echo "starting proxy" node ./web-api/proxy.js @@ -118,4 +121,4 @@ if [ ! -e "$CIRCLECI" ]; then pkill -P $ESEARCH_PID fi -pkill -P $S3RVER_PID \ No newline at end of file +pkill -P $S3RVER_PID diff --git a/set-tokens.sh b/set-tokens.sh index 3000dd7e4a6..9730e3f1ecc 100755 --- a/set-tokens.sh +++ b/set-tokens.sh @@ -13,8 +13,8 @@ response=$(aws cognito-idp admin-initiate-auth \ --client-id "${CLIENT_ID}" \ --region "${REGION}" \ --auth-flow ADMIN_NO_SRP_AUTH \ - --auth-parameters USERNAME="petitionsclerk1@example.com"',PASSWORD'="Testing1234$") + --auth-parameters USERNAME="petitionsclerk1@example.com"',PASSWORD'="Testing1234$") PETITIONS_CLERK_TOKEN=$(echo $response | jq -r '.AuthenticationResult.IdToken') -export PETITIONS_CLERK_TOKEN \ No newline at end of file +export PETITIONS_CLERK_TOKEN diff --git a/setup-zap-auth.sh b/setup-zap-auth.sh index 8715311e6b7..94dfe04fcb6 100755 --- a/setup-zap-auth.sh +++ b/setup-zap-auth.sh @@ -11,4 +11,4 @@ replacer.full_list(0).matchtype=REQ_HEADER replacer.full_list(0).matchstr=Authorization replacer.full_list(0).regex=false replacer.full_list(0).replacement=$BEARER_AUTH -EOF \ No newline at end of file +EOF diff --git a/shared/createModule.js b/shared/createModule.js index 900577ee33a..8920058465c 100644 --- a/shared/createModule.js +++ b/shared/createModule.js @@ -4,9 +4,6 @@ const fs = require('fs'); // USAGE EXAMPLE: node createModule.js path1/file1 path2/file2 const targets = [ 'shared/src/business/assets/ustcPdf.scss', - 'shared/src/business/useCaseHelper/caseConfirmation/caseConfirmation.pug', - 'shared/src/business/useCaseHelper/courtIssuedDocument/paperServiceAddressPage.pug', - 'shared/src/business/useCaseHelper/standingPretrialNotice/standingPretrialNotice.pug', 'shared/src/business/utilities/htmlGenerator/index.pug', 'shared/src/business/utilities/htmlGenerator/index.scss', 'shared/src/business/utilities/generateHTMLTemplateForPDF/noticeOfTrialIssued.pug', diff --git a/shared/src/authorization/authorizationClientService.js b/shared/src/authorization/authorizationClientService.js index 4a2fa8ceac6..24594086035 100644 --- a/shared/src/authorization/authorizationClientService.js +++ b/shared/src/authorization/authorizationClientService.js @@ -1,6 +1,7 @@ const ROLE_PERMISSIONS = { ADD_CASE_TO_TRIAL_SESSION: 'ADD_CASE_TO_TRIAL_SESSION', ADD_EDIT_PRACTITIONER_USER: 'ADD_EDIT_PRACTITIONER_USER', + ADD_EDIT_STATISTICS: 'ADD_EDIT_STATISTICS', ADVANCED_SEARCH: 'ADVANCED_SEARCH', ARCHIVE_DOCUMENT: 'ARCHIVE_DOCUMENT', ASSIGN_WORK_ITEM: 'ASSIGN_WORK_ITEM', @@ -27,6 +28,7 @@ const ROLE_PERMISSIONS = { GET_USERS_IN_SECTION: 'GET_USERS_IN_SECTION', JUDGES_NOTES: 'JUDGES_NOTES', MANAGE_PRACTITIONER_USERS: 'MANAGE_PRACTITIONER_USERS', + MESSAGES: 'MESSAGES', MIGRATE_CASE: 'MIGRATE_CASE', PENDING_ITEMS: 'PENDING_ITEMS', PETITION: 'PETITION', @@ -63,6 +65,7 @@ const allInternalUserPermissions = [ ROLE_PERMISSIONS.GET_CASE, ROLE_PERMISSIONS.GET_READ_MESSAGES, ROLE_PERMISSIONS.GET_USERS_IN_SECTION, + ROLE_PERMISSIONS.MESSAGES, ROLE_PERMISSIONS.PENDING_ITEMS, ROLE_PERMISSIONS.CASE_NOTES, ROLE_PERMISSIONS.PRIORITIZE_CASE, @@ -95,6 +98,7 @@ const AUTHORIZATION_MAP = { ], clerkofcourt: [ ...allInternalUserPermissions, + ROLE_PERMISSIONS.ADD_EDIT_STATISTICS, ROLE_PERMISSIONS.ASSIGN_WORK_ITEM, ROLE_PERMISSIONS.DOCKET_ENTRY, ROLE_PERMISSIONS.EDIT_PETITION_DETAILS, @@ -104,6 +108,7 @@ const AUTHORIZATION_MAP = { ], docketclerk: [ ...allInternalUserPermissions, + ROLE_PERMISSIONS.ADD_EDIT_STATISTICS, ROLE_PERMISSIONS.ASSIGN_WORK_ITEM, ROLE_PERMISSIONS.DOCKET_ENTRY, ROLE_PERMISSIONS.EDIT_DOCKET_ENTRY, @@ -148,6 +153,7 @@ const AUTHORIZATION_MAP = { ], petitionsclerk: [ ...allInternalUserPermissions, + ROLE_PERMISSIONS.ADD_EDIT_STATISTICS, ROLE_PERMISSIONS.ASSIGN_WORK_ITEM, ROLE_PERMISSIONS.CREATE_ORDER_DOCKET_ENTRY, ROLE_PERMISSIONS.SERVE_DOCUMENT, diff --git a/shared/src/authorization/authorizationClientService.test.js b/shared/src/authorization/authorizationClientService.test.js index b0221197b3c..3daafdb15fb 100644 --- a/shared/src/authorization/authorizationClientService.test.js +++ b/shared/src/authorization/authorizationClientService.test.js @@ -3,13 +3,13 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('./authorizationClientService'); -const { User } = require('../business/entities/User'); +const { ROLES } = require('../business/entities/EntityConstants'); describe('Authorization client service', () => { it('returns true for any user whose userId matches the 3rd owner argument, in this case "someUser" === "someUser"', () => { expect( isAuthorized( - { role: User.ROLES.petitioner, userId: 'someUser' }, + { role: ROLES.petitioner, userId: 'someUser' }, 'unknown action', 'someUser', ), @@ -19,7 +19,7 @@ describe('Authorization client service', () => { it('should authorize a petitionsclerk for getCase', () => { expect( isAuthorized( - { role: User.ROLES.petitionsClerk, userId: 'petitionsclerk' }, + { role: ROLES.petitionsClerk, userId: 'petitionsclerk' }, ROLE_PERMISSIONS.GET_CASE, ), ).toBeTruthy(); @@ -28,7 +28,7 @@ describe('Authorization client service', () => { it("should return false when a user doesn't have a petitionsclerk role", () => { expect( isAuthorized( - { role: User.ROLES.petitioner, userId: 'someUser' }, + { role: ROLES.petitioner, userId: 'someUser' }, ROLE_PERMISSIONS.GET_CASES_BY_STATUS, ), ).toBeFalsy(); @@ -37,7 +37,7 @@ describe('Authorization client service', () => { it('should authorize a petitions clerk for work items', () => { expect( isAuthorized( - { role: User.ROLES.petitionsClerk, userId: 'petitionsclerk' }, + { role: ROLES.petitionsClerk, userId: 'petitionsclerk' }, ROLE_PERMISSIONS.WORKITEM, ), ).toBeTruthy(); @@ -46,7 +46,7 @@ describe('Authorization client service', () => { it('should authorize a petitions clerk for start a case from paper', () => { expect( isAuthorized( - { role: User.ROLES.petitionsClerk, userId: 'petitionsclerk' }, + { role: ROLES.petitionsClerk, userId: 'petitionsclerk' }, ROLE_PERMISSIONS.START_PAPER_CASE, ), ).toBeTruthy(); @@ -55,7 +55,7 @@ describe('Authorization client service', () => { it('should authorize a docket clerk for work items', () => { expect( isAuthorized( - { role: User.ROLES.docketClerk, userId: 'docketclerk' }, + { role: ROLES.docketClerk, userId: 'docketclerk' }, ROLE_PERMISSIONS.WORKITEM, ), ).toBeTruthy(); @@ -64,7 +64,7 @@ describe('Authorization client service', () => { it('should authorize an adc user for work items', () => { expect( isAuthorized( - { role: User.ROLES.adc, userId: 'adc' }, + { role: ROLES.adc, userId: 'adc' }, ROLE_PERMISSIONS.WORKITEM, ), ).toBeTruthy(); @@ -73,7 +73,7 @@ describe('Authorization client service', () => { it('should authorize an irsPractitioner for getCase', () => { expect( isAuthorized( - { role: User.ROLES.irsPractitioner, userId: 'irsPractitioner' }, + { role: ROLES.irsPractitioner, userId: 'irsPractitioner' }, ROLE_PERMISSIONS.GET_CASE, ), ).toBeTruthy(); @@ -82,7 +82,7 @@ describe('Authorization client service', () => { it('should authorize a docketclerk for update case', () => { expect( isAuthorized( - { role: User.ROLES.docketClerk, userId: 'docketclerk' }, + { role: ROLES.docketClerk, userId: 'docketclerk' }, ROLE_PERMISSIONS.UPDATE_CASE, ), ).toBeTruthy(); @@ -91,7 +91,7 @@ describe('Authorization client service', () => { it('should evaluate owner when the owner param is provided', () => { expect( isAuthorized( - { role: User.ROLES.docketClerk, userId: '123456' }, + { role: ROLES.docketClerk, userId: '123456' }, ROLE_PERMISSIONS.UPDATE_CASE, 123456, ), diff --git a/shared/src/authorization/getUserPermissions.test.js b/shared/src/authorization/getUserPermissions.test.js index d222c5b26d5..bfb32cc5862 100644 --- a/shared/src/authorization/getUserPermissions.test.js +++ b/shared/src/authorization/getUserPermissions.test.js @@ -1,11 +1,11 @@ +import { ROLES } from '../business/entities/EntityConstants'; import { ROLE_PERMISSIONS } from './authorizationClientService'; -import { User } from '../business/entities/User'; import { getUserPermissions } from './getUserPermissions'; describe('getUserPermissions', () => { it('returns an object containing ROLE_PERMISSIONS and a boolean value based on the given user role', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }; const permissions = getUserPermissions(user); diff --git a/shared/src/business/entities/CaseMessage.js b/shared/src/business/entities/CaseMessage.js new file mode 100644 index 00000000000..1d5434e9c46 --- /dev/null +++ b/shared/src/business/entities/CaseMessage.js @@ -0,0 +1,139 @@ +const joi = require('@hapi/joi'); +const { + joiValidationDecorator, +} = require('../../utilities/JoiValidationDecorator'); +const { createISODateString } = require('../utilities/DateHandler'); +const { getTimestampSchema } = require('../../utilities/dateSchema'); +const joiStrictTimestamp = getTimestampSchema(); +const { + CHAMBERS_SECTIONS, + DOCKET_NUMBER_MATCHER, + SECTIONS, +} = require('./EntityConstants'); + +/** + * constructor + * + * @param {object} rawMessage the raw message data + * @constructor + */ +function CaseMessage(rawMessage, { applicationContext }) { + if (!applicationContext) { + throw new TypeError('applicationContext must be defined'); + } + + this.attachments = rawMessage.attachments; + this.caseId = rawMessage.caseId; + this.caseStatus = rawMessage.caseStatus; + this.createdAt = rawMessage.createdAt || createISODateString(); + this.docketNumber = rawMessage.docketNumber; + this.docketNumberWithSuffix = rawMessage.docketNumberWithSuffix; + this.entityName = 'CaseMessage'; + this.from = rawMessage.from; + this.fromSection = rawMessage.fromSection; + this.fromUserId = rawMessage.fromUserId; + this.message = rawMessage.message; + this.messageId = rawMessage.messageId || applicationContext.getUniqueId(); + this.subject = rawMessage.subject; + this.to = rawMessage.to; + this.toSection = rawMessage.toSection; + this.toUserId = rawMessage.toUserId; +} + +CaseMessage.validationName = 'CaseMessage'; + +CaseMessage.VALIDATION_ERROR_MESSAGES = { + message: 'Enter a message', + subject: 'Enter a subject line', + toSection: 'Select a section', + toUserId: 'Select a recipient', +}; + +CaseMessage.VALIDATION_RULES = { + attachments: joi + .array() + .optional() + .description('Array of document metadata objects attached to the message.'), + caseId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required() + .description('ID of the case the message is attached to.'), + caseStatus: joi + .string() + .optional() + .description('The status of the associated case.'), + createdAt: joiStrictTimestamp + .required() + .description('When the message was created.'), + docketNumber: joi.string().regex(DOCKET_NUMBER_MATCHER).required(), + docketNumberWithSuffix: joi + .string() + .allow(null) + .optional() + .description('The docket number and suffix for the associated case.'), + entityName: joi.string().valid('CaseMessage').required(), + from: joi + .string() + .max(100) + .required() + .description('The name of the user who sent the message.'), + fromSection: joi + .string() + .valid(...SECTIONS, ...CHAMBERS_SECTIONS) + .required() + .description('The section of the user who sent the message.'), + fromUserId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required() + .description('The ID of the user who sent the message.'), + message: joi.string().max(500).required().description('The message text.'), + messageId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required() + .description( + 'A unique ID generated by the system to represent the message.', + ), + subject: joi + .string() + .max(250) + .required() + .description('The subject line of the message.'), + to: joi + .string() + .max(100) + .required() + .allow(null) + .description('The name of the user who is the recipient of the message.'), + toSection: joi + .string() + .valid(...SECTIONS, ...CHAMBERS_SECTIONS) + .required() + .description( + 'The section of the user who is the recipient of the message.', + ), + toUserId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required() + .allow(null) + .description('The ID of the user who is the recipient of the message.'), +}; + +joiValidationDecorator( + CaseMessage, + joi.object().keys(CaseMessage.VALIDATION_RULES), + CaseMessage.VALIDATION_ERROR_MESSAGES, +); + +module.exports = { CaseMessage }; diff --git a/shared/src/business/entities/CaseMessage.test.js b/shared/src/business/entities/CaseMessage.test.js new file mode 100644 index 00000000000..954fde1c921 --- /dev/null +++ b/shared/src/business/entities/CaseMessage.test.js @@ -0,0 +1,46 @@ +const { applicationContext } = require('../test/createTestApplicationContext'); +const { CaseMessage } = require('./CaseMessage'); + +describe('CaseMessage', () => { + describe('isValid', () => { + it('should throw an error if app context is not passed in', () => { + expect(() => new CaseMessage({}, {})).toThrow(); + }); + + it('creates a valid CaseMessage without messageId (defaults to new uuid)', () => { + const message = new CaseMessage( + { + caseId: '3079c990-cc6c-4b99-8fca-8e31f2d9e7a8', + caseStatus: 'General Docket - Not at Issue', + createdAt: '2019-01-01T17:29:13.122Z', + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + from: 'gg', + fromSection: 'petitions', + fromUserId: '6805d1ab-18d0-43ec-bafb-654e83405416', + message: 'hello world', + subject: 'hey!', + to: 'bob', + toSection: 'petitions', + toUserId: '6805d1ab-18d0-43ec-bafb-654e83405416', + }, + { applicationContext }, + ); + expect(message.isValid()).toBeTruthy(); + }); + + it('creates an invalid CaseMessage with no message', () => { + const message = new CaseMessage( + { + caseId: '3079c990-cc6c-4b99-8fca-8e31f2d9e7a8', + from: 'gg', + fromSection: 'petitions', + fromUserId: '6805d1ab-18d0-43ec-bafb-654e83405416', + subject: 'hey!', + }, + { applicationContext }, + ); + expect(message.isValid()).toBeFalsy(); + }); + }); +}); diff --git a/shared/src/business/entities/Correspondence.js b/shared/src/business/entities/Correspondence.js index 2f07e0e502f..2ee3a9bdc7b 100644 --- a/shared/src/business/entities/Correspondence.js +++ b/shared/src/business/entities/Correspondence.js @@ -19,14 +19,24 @@ function Correspondence(rawProps) { } Correspondence.schema = { - documentId: joi.string().required(), - documentTitle: joi.string().required(), - filedBy: joi.string().allow('').optional(), + documentId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required(), + documentTitle: joi.string().max(500).required(), + filedBy: joi.string().max(500).allow('').optional(), filingDate: joiStrictTimestamp .max('now') .required() .description('Date that this Document was filed.'), - userId: joi.string().required(), + userId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required(), }; joiValidationDecorator(Correspondence, Correspondence.schema, {}); diff --git a/shared/src/business/entities/Correspondence.test.js b/shared/src/business/entities/Correspondence.test.js index f0b00fac08f..af34db24bad 100644 --- a/shared/src/business/entities/Correspondence.test.js +++ b/shared/src/business/entities/Correspondence.test.js @@ -10,7 +10,11 @@ describe('Correspondence', () => { it('should be valid when all fields are present', () => { const correspondence = new Correspondence( - { documentId: '123', documentTitle: 'A Title', userId: '111' }, + { + documentId: 'e9ab90a9-2150-4dd1-90b4-fee2097c23db', + documentTitle: 'A Title', + userId: 'a389ca07-f19e-45d4-8e77-5cb79c9285ae', + }, { applicationContext }, ); expect(correspondence.isValid()).toBeTruthy(); diff --git a/shared/src/business/entities/DocketRecord.js b/shared/src/business/entities/DocketRecord.js index a20d45bac61..90a3ae77d15 100644 --- a/shared/src/business/entities/DocketRecord.js +++ b/shared/src/business/entities/DocketRecord.js @@ -25,6 +25,7 @@ function DocketRecord(rawDocketRecord, { applicationContext }) { this.documentId = rawDocketRecord.documentId; this.editState = rawDocketRecord.editState; this.eventCode = rawDocketRecord.eventCode; + this.numberOfPages = rawDocketRecord.numberOfPages; this.filedBy = rawDocketRecord.filedBy; this.filingDate = rawDocketRecord.filingDate; this.index = rawDocketRecord.index; @@ -45,11 +46,13 @@ joiValidationDecorator( joi.object().keys({ action: joi .string() + .max(100) .optional() .allow(null) .description('Action taken in response to this Docket Record item.'), description: joi .string() + .max(500) .required() .description( 'Text that describes this Docket Record item, which may be part of the Filings and Proceedings value.', @@ -64,6 +67,7 @@ joiValidationDecorator( .description('ID of the associated PDF document in the S3 bucket.'), editState: joi .string() + .max(3000) .allow(null) .optional() .meta({ tags: ['Restricted'] }) @@ -78,10 +82,11 @@ joiValidationDecorator( ), filedBy: joi .string() + .max(500) .optional() .allow(null) .meta({ tags: ['Restricted'] }) - .description('ID of the user that filed this Docket Record item.'), + .description('User that filed this Docket Record item.'), filingDate: joiStrictTimestamp .max('now') .required() @@ -91,8 +96,10 @@ joiValidationDecorator( .integer() .required() .description('Index of this item in the Docket Record list.'), + numberOfPages: joi.number().optional().allow(null), servedPartiesCode: joi .string() + .valid('R', 'B', '') .allow(null) .optional() .description('Served parties code to override system-computed code.'), @@ -100,4 +107,8 @@ joiValidationDecorator( DocketRecord.VALIDATION_ERROR_MESSAGES, ); +DocketRecord.prototype.setNumberOfPages = function (numberOfPages) { + this.numberOfPages = numberOfPages; +}; + module.exports = { DocketRecord }; diff --git a/shared/src/business/entities/Document.js b/shared/src/business/entities/Document.js index 1eaa76b0d69..20ddc1d4466 100644 --- a/shared/src/business/entities/Document.js +++ b/shared/src/business/entities/Document.js @@ -1,97 +1,31 @@ -const courtIssuedEventCodes = require('../../tools/courtIssuedEventCodes.json'); -const documentMapExternal = require('../../tools/externalFilingEvents.json'); -const documentMapInternal = require('../../tools/internalFilingEvents.json'); const joi = require('@hapi/joi'); +const { + COURT_ISSUED_EVENT_CODES, + DOCKET_NUMBER_MATCHER, + DOCUMENT_CATEGORY_MAP, + DOCUMENT_INTERNAL_CATEGORY_MAP, + DOCUMENT_RELATIONSHIPS, + INITIAL_DOCUMENT_TYPES, + OBJECTIONS_OPTIONS, + ORDER_TYPES, + PRACTITIONER_ASSOCIATION_DOCUMENT_TYPES, + SCENARIOS, + SIGNED_DOCUMENT_TYPES, + SYSTEM_GENERATED_DOCUMENT_TYPES, + TRACKED_DOCUMENT_TYPES, +} = require('./EntityConstants'); const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); const { createISODateString } = require('../utilities/DateHandler'); -const { DOCKET_NUMBER_MATCHER } = require('./cases/CaseConstants'); const { flatten } = require('lodash'); const { getTimestampSchema } = require('../../utilities/dateSchema'); -const { Order } = require('./orders/Order'); const { User } = require('./User'); const { WorkItem } = require('./WorkItem'); const joiStrictTimestamp = getTimestampSchema(); -Document.CATEGORIES = Object.keys(documentMapExternal); -Document.CATEGORY_MAP = documentMapExternal; -Document.NOTICE_EVENT_CODES = ['NOT']; -Document.COURT_ISSUED_EVENT_CODES = courtIssuedEventCodes; -Document.INTERNAL_CATEGORIES = Object.keys(documentMapInternal); -Document.INTERNAL_CATEGORY_MAP = documentMapInternal; -Document.PETITION_DOCUMENT_TYPES = ['Petition']; -Document.OPINION_DOCUMENT_TYPES = ['MOP', 'SOP', 'TCOP']; -Document.ORDER_DOCUMENT_TYPES = [ - 'O', - 'OAJ', - 'OAL', - 'OAP', - 'OAPF', - 'OAR', - 'OAS', - 'OASL', - 'OAW', - 'OAX', - 'OCA', - 'OD', - 'ODD', - 'ODL', - 'ODP', - 'ODR', - 'ODS', - 'ODSL', - 'ODW', - 'ODX', - 'OF', - 'OFAB', - 'OFFX', - 'OFWD', - 'OFX', - 'OIP', - 'OJR', - 'OODS', - 'OPFX', - 'OPX', - 'ORAP', - 'OROP', - 'OSC', - 'OSCP', - 'OST', - 'OSUB', - 'OAD', - 'ODJ', -]; Document.validationName = 'Document'; -Document.SCENARIOS = [ - 'Standard', - 'Nonstandard A', - 'Nonstandard B', - 'Nonstandard C', - 'Nonstandard D', - 'Nonstandard E', - 'Nonstandard F', - 'Nonstandard G', - 'Nonstandard H', - 'Type A', - 'Type B', - 'Type C', - 'Type D', - 'Type E', - 'Type F', - 'Type G', - 'Type H', -]; - -Document.RELATIONSHIPS = [ - 'primaryDocument', - 'primarySupportingDocument', - 'secondaryDocument', - 'secondarySupportingDocument', - 'supportingDocument', -]; - /** * constructor * @@ -133,7 +67,6 @@ function Document(rawDocument, { applicationContext, filtered = false }) { this.addToCoversheet = rawDocument.addToCoversheet; this.archived = rawDocument.archived; this.attachments = rawDocument.attachments; - this.caseId = rawDocument.caseId; this.certificateOfService = rawDocument.certificateOfService; this.certificateOfServiceDate = rawDocument.certificateOfServiceDate; this.createdAt = rawDocument.createdAt || createISODateString(); @@ -189,140 +122,37 @@ function Document(rawDocument, { applicationContext, filtered = false }) { this.generateFiledBy(rawDocument); } -const practitionerAssociationDocumentTypes = [ - 'Entry of Appearance', - 'Substitution of Counsel', -]; - -/** - * documentTypes - * - * @type {{petitionFile: string, requestForPlaceOfTrial: string, stin: string}} - */ -Document.INITIAL_DOCUMENT_TYPES = { - applicationForWaiverOfFilingFee: { - documentType: 'Application for Waiver of Filing Fee', - eventCode: 'APW', - }, - ownershipDisclosure: { - documentType: 'Ownership Disclosure Statement', - eventCode: 'DISC', - }, - petition: { - documentType: 'Petition', - eventCode: 'P', - }, - requestForPlaceOfTrial: { - documentTitle: 'Request for Place of Trial at [Place]', - documentType: 'Request for Place of Trial', - eventCode: 'RQT', - }, - stin: { - documentType: 'Statement of Taxpayer Identification', - eventCode: 'STIN', - }, -}; - -Document.NOTICE_OF_DOCKET_CHANGE = { - documentTitle: 'Notice of Docket Change for Docket Entry No. [Index]', - documentType: 'Notice of Docket Change', - eventCode: 'NODC', -}; - -Document.NOTICE_OF_TRIAL = { - documentTitle: 'Notice of Trial on [Date] at [Time]', - documentType: 'Notice of Trial', - eventCode: 'NDT', -}; - -Document.STANDING_PRETRIAL_NOTICE = { - documentTitle: 'Standing Pretrial Notice', - documentType: 'Standing Pretrial Notice', - eventCode: 'SPTN', -}; - -Document.STANDING_PRETRIAL_ORDER = { - documentTitle: 'Standing Pretrial Order', - documentType: 'Standing Pretrial Order', - eventCode: 'SPTO', -}; - -Document.SYSTEM_GENERATED_DOCUMENT_TYPES = { - noticeOfDocketChange: Document.NOTICE_OF_DOCKET_CHANGE, - noticeOfTrial: Document.NOTICE_OF_TRIAL, - standingPretrialNotice: Document.STANDING_PRETRIAL_NOTICE, - standingPretrialOrder: Document.STANDING_PRETRIAL_ORDER, -}; - -Document.SIGNED_DOCUMENT_TYPES = { - signedStipulatedDecision: { - documentType: 'Stipulated Decision', - eventCode: 'SDEC', - }, -}; - -Document.TRACKED_DOCUMENT_TYPES = { - application: { - category: 'Application', - }, - motion: { - category: 'Motion', - }, - orderToShowCause: { - documentType: 'Order to Show Cause', - eventCode: 'OSC', - }, - proposedStipulatedDecision: { - documentType: 'Proposed Stipulated Decision', - eventCode: 'PSDE', - }, -}; - -Document.CONTACT_CHANGE_DOCUMENT_TYPES = [ - 'Notice of Change of Address', - 'Notice of Change of Telephone Number', - 'Notice of Change of Address and Telephone Number', -]; - -Document.TRANSCRIPT_EVENT_CODE = 'TRAN'; - Document.isPendingOnCreation = rawDocument => { - const isPending = Object.values(Document.TRACKED_DOCUMENT_TYPES).some( - trackedType => { - return ( - (rawDocument.category && - trackedType.category === rawDocument.category) || - (rawDocument.eventCode && - trackedType.eventCode === rawDocument.eventCode) - ); - }, - ); + const isPending = Object.values(TRACKED_DOCUMENT_TYPES).some(trackedType => { + return ( + (rawDocument.category && trackedType.category === rawDocument.category) || + (rawDocument.eventCode && trackedType.eventCode === rawDocument.eventCode) + ); + }); return isPending; }; Document.getDocumentTypes = () => { const allFilingEvents = flatten([ - ...Object.values(documentMapExternal), - ...Object.values(documentMapInternal), + ...Object.values(DOCUMENT_CATEGORY_MAP), + ...Object.values(DOCUMENT_INTERNAL_CATEGORY_MAP), ]); const filingEventTypes = allFilingEvents.map(t => t.documentType); - const orderDocTypes = Order.ORDER_TYPES.map(t => t.documentType); - const courtIssuedDocTypes = Document.COURT_ISSUED_EVENT_CODES.map( - t => t.documentType, + const orderDocTypes = ORDER_TYPES.map(t => t.documentType); + const courtIssuedDocTypes = COURT_ISSUED_EVENT_CODES.map(t => t.documentType); + const initialTypes = Object.keys(INITIAL_DOCUMENT_TYPES).map( + t => INITIAL_DOCUMENT_TYPES[t].documentType, ); - const initialTypes = Object.keys(Document.INITIAL_DOCUMENT_TYPES).map( - t => Document.INITIAL_DOCUMENT_TYPES[t].documentType, + const signedTypes = Object.keys(SIGNED_DOCUMENT_TYPES).map( + t => SIGNED_DOCUMENT_TYPES[t].documentType, ); - const signedTypes = Object.keys(Document.SIGNED_DOCUMENT_TYPES).map( - t => Document.SIGNED_DOCUMENT_TYPES[t].documentType, + const systemGeneratedTypes = Object.keys(SYSTEM_GENERATED_DOCUMENT_TYPES).map( + t => SYSTEM_GENERATED_DOCUMENT_TYPES[t].documentType, ); - const systemGeneratedTypes = Object.keys( - Document.SYSTEM_GENERATED_DOCUMENT_TYPES, - ).map(t => Document.SYSTEM_GENERATED_DOCUMENT_TYPES[t].documentType); const documentTypes = [ ...initialTypes, - ...practitionerAssociationDocumentTypes, + ...PRACTITIONER_ASSOCIATION_DOCUMENT_TYPES, ...filingEventTypes, ...orderDocTypes, ...courtIssuedDocTypes, @@ -333,53 +163,18 @@ Document.getDocumentTypes = () => { return documentTypes; }; -/** - * - * @returns {Array} event codes defined in the Document entity - */ -Document.eventCodes = [ - Document.INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee.eventCode, - Document.INITIAL_DOCUMENT_TYPES.ownershipDisclosure.eventCode, - Document.INITIAL_DOCUMENT_TYPES.petition.eventCode, - Document.INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, - Document.INITIAL_DOCUMENT_TYPES.stin.eventCode, - Document.NOTICE_OF_DOCKET_CHANGE.eventCode, - Document.NOTICE_OF_TRIAL.eventCode, - Document.STANDING_PRETRIAL_NOTICE.eventCode, - Document.STANDING_PRETRIAL_ORDER.eventCode, - // TODO: Move these constants - 'MISL', - 'FEE', - 'FEEW', - 'MGRTED', - 'MIND', - 'MINC', -]; - -/** - * - * @returns {boolean} true if the document is a petition document type, false otherwise - */ -Document.prototype.isPetitionDocument = function () { - return Document.PETITION_DOCUMENT_TYPES.includes(this.documentType); -}; - joiValidationDecorator( Document, joi.object().keys({ addToCoversheet: joi.boolean().optional(), - additionalInfo: joi.string().optional(), - additionalInfo2: joi.string().optional(), + additionalInfo: joi.string().max(500).optional(), + additionalInfo2: joi.string().max(500).optional(), archived: joi .boolean() .optional() .description( 'A document that was archived instead of added to the Docket Record.', ), - caseId: joi - .string() - .optional() - .description('Unique ID of the associated Case.'), certificateOfService: joi.boolean().optional(), certificateOfServiceDate: joi.when('certificateOfService', { is: true, @@ -404,6 +199,7 @@ joiValidationDecorator( .description('Docket Number of the associated Case in XXXXX-YY format.'), docketNumbers: joi .string() + .max(500) .optional() .description( 'Optional Docket Number text used when generating a fully concatenated document title.', @@ -424,6 +220,7 @@ joiValidationDecorator( .description('ID of the associated PDF document in the S3 bucket.'), documentTitle: joi .string() + .max(500) .optional() .description('The title of this document.'), documentType: joi @@ -434,13 +231,13 @@ joiValidationDecorator( draftState: joi.object().allow(null).optional(), entityName: joi.string().valid('Document').required(), eventCode: joi.string().optional(), - filedBy: joi.string().allow('').optional(), + filedBy: joi.string().max(500).allow('').optional(), filingDate: joiStrictTimestamp .max('now') .required() .description('Date that this Document was filed.'), - freeText: joi.string().optional(), - freeText2: joi.string().optional(), + freeText: joi.string().max(500).optional(), + freeText2: joi.string().max(500).optional(), hasSupportingDocuments: joi.boolean().optional(), isFileAttached: joi.boolean().optional(), isPaper: joi.boolean().optional(), @@ -456,7 +253,10 @@ joiValidationDecorator( 'A lodged document is awaiting action by the judge to enact or refuse.', ), numberOfPages: joi.number().optional().allow(null), - objections: joi.string().optional(), + objections: joi + .string() + .valid(...OBJECTIONS_OPTIONS) + .optional(), ordinalValue: joi.string().optional(), partyIrsPractitioner: joi.boolean().optional(), partyPrimary: joi @@ -471,22 +271,28 @@ joiValidationDecorator( previousDocument: joi.object().optional(), privatePractitioners: joi .array() - .items({ name: joi.string().required() }) + .items({ name: joi.string().max(500).required() }) .optional() .description( 'Practitioner names to be used to compose the filedBy text.', ), processingStatus: joi.string().optional(), qcAt: joiStrictTimestamp.optional(), - qcByUserId: joi.string().optional().allow(null), + qcByUserId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .optional() + .allow(null), receivedAt: joiStrictTimestamp.optional(), relationship: joi .string() - .valid(...Document.RELATIONSHIPS) + .valid(...DOCUMENT_RELATIONSHIPS) .optional(), scenario: joi .string() - .valid(...Document.SCENARIOS) + .valid(...SCENARIOS) .optional(), secondaryDate: joiStrictTimestamp .optional() @@ -498,7 +304,7 @@ joiValidationDecorator( .description('When the document is served on the parties.'), servedParties: joi .array() - .items({ name: joi.string().required() }) + .items({ name: joi.string().max(500).required() }) .optional(), serviceDate: joiStrictTimestamp .max('now') @@ -507,7 +313,13 @@ joiValidationDecorator( .description('Certificate of service date.'), serviceStamp: joi.string().optional(), signedAt: joiStrictTimestamp.optional().allow(null), - signedByUserId: joi.string().optional().allow(null), + signedByUserId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .optional() + .allow(null), signedJudgeName: joi.string().optional().allow(null), supportingDocument: joi.string().optional().allow(null), trialLocation: joi @@ -517,7 +329,12 @@ joiValidationDecorator( .description( 'An optional trial location used when generating a fully concatenated document title.', ), - userId: joi.string().required(), + userId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required(), workItems: joi.array().optional(), }), ); @@ -631,14 +448,14 @@ Document.prototype.getQCWorkItem = function () { }; Document.prototype.isAutoServed = function () { - const externalDocumentTypes = flatten(Object.values(documentMapExternal)).map( - t => t.documentType, - ); + const externalDocumentTypes = flatten( + Object.values(DOCUMENT_CATEGORY_MAP), + ).map(t => t.documentType); const isExternalDocumentType = externalDocumentTypes.includes( this.documentType, ); - const isPractitionerAssociationDocumentType = practitionerAssociationDocumentTypes.includes( + const isPractitionerAssociationDocumentType = PRACTITIONER_ASSOCIATION_DOCUMENT_TYPES.includes( this.documentType, ); //if fully concatenated document title includes the word Simultaneous, do not auto-serve @@ -652,4 +469,20 @@ Document.prototype.isAutoServed = function () { ); }; +Document.prototype.setNumberOfPages = function (numberOfPages) { + this.numberOfPages = numberOfPages; +}; + +/** + * retrieves formatted document type (stripped eventCode, without the dash). + * if it's TCOP - TC Opinion, it retrieves TC Opinion. + * if it's Summary Opinion, then it returns Summary Opinion + * + * @param {string} documentType document type to strip the event code + * @returns {string} formatted document type + */ +Document.getFormattedType = function (documentType) { + return documentType.split('-').slice(-1).join('').trim(); +}; + exports.Document = Document; diff --git a/shared/src/business/entities/Document.test.js b/shared/src/business/entities/Document.test.js index 23da7aa68ca..46f30175e51 100644 --- a/shared/src/business/entities/Document.test.js +++ b/shared/src/business/entities/Document.test.js @@ -1,13 +1,13 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { Document } = require('./Document'); const { Message } = require('./Message'); -const { User } = require('./User'); +const { ROLES } = require('./EntityConstants'); const { WorkItem } = require('./WorkItem'); const A_VALID_DOCUMENT = { documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '02323349-87fe-4d29-91fe-8dd6916d2fda', }; const caseDetail = { contactPrimary: { @@ -102,7 +102,7 @@ describe('Document entity', () => { it('Creates an invalid document with no document type', () => { const myDoc = new Document( { - userId: '123', + userId: '02323349-87fe-4d29-91fe-8dd6916d2fda', }, { applicationContext }, ); @@ -133,7 +133,7 @@ describe('Document entity', () => { const myDoc = new Document(A_VALID_DOCUMENT, { applicationContext }); const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', caseStatus: 'new', @@ -837,10 +837,15 @@ describe('Document entity', () => { describe('setQCed', () => { it('updates the document QC information with user name, id, and date', () => { const document = new Document(A_VALID_DOCUMENT, { applicationContext }); - const user = { name: 'Jean Luc', userId: 'ncc-1701-c' }; + const user = { + name: 'Jean Luc', + userId: '02323349-87fe-4d29-91fe-8dd6916d2fda', + }; document.setQCed(user); expect(document.qcByUser.name).toEqual('Jean Luc'); - expect(document.qcByUser.userId).toEqual('ncc-1701-c'); + expect(document.qcByUser.userId).toEqual( + '02323349-87fe-4d29-91fe-8dd6916d2fda', + ); expect(document.qcAt).toBeDefined(); }); }); @@ -852,7 +857,7 @@ describe('Document entity', () => { ...A_VALID_DOCUMENT, workItems: [ { - assigneeId: 'bill', + assigneeId: '49b4789b-3c90-4940-946c-95a700d5a501', assigneeName: 'bill', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', caseStatus: 'new', @@ -872,7 +877,7 @@ describe('Document entity', () => { workItemId: 'dda4acce-7b0f-40e2-b5a7-261b5c0dee28', }, { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', caseStatus: 'new', @@ -907,7 +912,7 @@ describe('Document entity', () => { ...A_VALID_DOCUMENT, workItems: [ { - assigneeId: 'bill', + assigneeId: '49b4789b-3c90-4940-946c-95a700d5a501', assigneeName: 'bill', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', caseStatus: 'new', @@ -1024,4 +1029,18 @@ describe('Document entity', () => { expect(document.servedParties).toMatchObject([{ name: 'Served Party' }]); }); }); + + describe('getFormattedType', () => { + it('strips out the dash and returns the verbiage after it', () => { + expect(Document.getFormattedType('TCOP - T.C. Opinion')).toEqual( + 'T.C. Opinion', + ); + }); + + it("returns the verbiage if there's no dash", () => { + expect(Document.getFormattedType('Summary Opinion')).toEqual( + 'Summary Opinion', + ); + }); + }); }); diff --git a/shared/src/business/entities/EntityConstants.js b/shared/src/business/entities/EntityConstants.js new file mode 100644 index 00000000000..1aff4289117 --- /dev/null +++ b/shared/src/business/entities/EntityConstants.js @@ -0,0 +1,781 @@ +const courtIssuedEventCodes = require('../../tools/courtIssuedEventCodes.json'); +const documentMapExternal = require('../../tools/externalFilingEvents.json'); +const documentMapInternal = require('../../tools/internalFilingEvents.json'); +const { sortBy } = require('lodash'); + +const SERVICE_INDICATOR_TYPES = { + SI_ELECTRONIC: 'Electronic', + SI_NONE: 'None', + SI_PAPER: 'Paper', +}; + +const DOCKET_NUMBER_MATCHER = /^([1-9]\d{2,4}-\d{2})$/; + +const TRIAL_LOCATION_MATCHER = /^[a-zA-Z ]+, [a-zA-Z ]+, [0-9]+$/; + +const CHIEF_JUDGE = 'Chief Judge'; + +const DOCKET_NUMBER_SUFFIXES = ['W', 'P', 'X', 'R', 'SL', 'L', 'S']; + +const CASE_STATUS_TYPES = { + assignedCase: 'Assigned - Case', // Case has been assigned to a judge + assignedMotion: 'Assigned - Motion', // Someone has requested a judge for the case + calendared: 'Calendared', // Case has been scheduled for trial + cav: 'CAV', // Core alternative valuation + closed: 'Closed', // Judge has made a ruling to close the case + generalDocket: 'General Docket - Not at Issue', // Submitted to the IRS + generalDocketReadyForTrial: 'General Docket - At Issue (Ready for Trial)', // Case is ready for trial + jurisdictionRetained: 'Jurisdiction Retained', // Jurisdiction of a case is retained by a specific judge — usually after the case is on a judge’s trial calendar + new: 'New', // Case has not been QCed + onAppeal: 'On Appeal', // After the trial, the case has gone to the appeals court + rule155: 'Rule 155', // Where the Court has filed or stated its opinion or issued a dispositive order determining the issues in a case, it may withhold entry of its decision for the purpose of permitting the parties to submit computations pursuant to the Court’s determination of the issues, showing the correct amount to be included in the decision. + submitted: 'Submitted', // Submitted to the judge for decision +}; + +const DOCUMENT_RELATIONSHIPS = [ + 'primaryDocument', + 'primarySupportingDocument', + 'secondaryDocument', + 'secondarySupportingDocument', + 'supportingDocument', +]; + +const ORDER_DOCUMENT_TYPES = [ + 'O', + 'OAJ', + 'OAL', + 'OAP', + 'OAPF', + 'OAR', + 'OAS', + 'OASL', + 'OAW', + 'OAX', + 'OCA', + 'OD', + 'ODD', + 'ODL', + 'ODP', + 'ODR', + 'ODS', + 'ODSL', + 'ODW', + 'ODX', + 'OF', + 'OFAB', + 'OFFX', + 'OFWD', + 'OFX', + 'OIP', + 'OJR', + 'OODS', + 'OPFX', + 'OPX', + 'ORAP', + 'OROP', + 'OSC', + 'OSCP', + 'OST', + 'OSUB', + 'OAD', + 'ODJ', +]; + +const DOCUMENT_NOTICE_EVENT_CODES = ['NOT']; +const DOCUMENT_CATEGORIES = Object.keys(documentMapExternal); +const DOCUMENT_CATEGORY_MAP = documentMapExternal; +const DOCUMENT_INTERNAL_CATEGORIES = Object.keys(documentMapInternal); +const DOCUMENT_INTERNAL_CATEGORY_MAP = documentMapInternal; +const COURT_ISSUED_EVENT_CODES = courtIssuedEventCodes; +const OPINION_DOCUMENT_TYPES = ['MOP', 'SOP', 'TCOP']; + +const SCENARIOS = [ + 'Standard', + 'Nonstandard A', + 'Nonstandard B', + 'Nonstandard C', + 'Nonstandard D', + 'Nonstandard E', + 'Nonstandard F', + 'Nonstandard G', + 'Nonstandard H', + 'Type A', + 'Type B', + 'Type C', + 'Type D', + 'Type E', + 'Type F', + 'Type G', + 'Type H', +]; + +const TRANSCRIPT_EVENT_CODE = 'TRAN'; + +const OBJECTIONS_OPTIONS = ['No', 'Yes', 'Unknown']; + +const CONTACT_CHANGE_DOCUMENT_TYPES = [ + 'Notice of Change of Address', + 'Notice of Change of Telephone Number', + 'Notice of Change of Address and Telephone Number', +]; + +const TRACKED_DOCUMENT_TYPES = { + application: { + category: 'Application', + }, + motion: { + category: 'Motion', + }, + orderToShowCause: { + documentType: 'Order to Show Cause', + eventCode: 'OSC', + }, + proposedStipulatedDecision: { + documentType: 'Proposed Stipulated Decision', + eventCode: 'PSDE', + }, +}; + +const INITIAL_DOCUMENT_TYPES = { + applicationForWaiverOfFilingFee: { + documentType: 'Application for Waiver of Filing Fee', + eventCode: 'APW', + }, + ownershipDisclosure: { + documentType: 'Ownership Disclosure Statement', + eventCode: 'DISC', + }, + petition: { + documentType: 'Petition', + eventCode: 'P', + }, + requestForPlaceOfTrial: { + documentTitle: 'Request for Place of Trial at [Place]', + documentType: 'Request for Place of Trial', + eventCode: 'RQT', + }, + stin: { + documentType: 'Statement of Taxpayer Identification', + eventCode: 'STIN', + }, +}; + +const NOTICE_OF_DOCKET_CHANGE = { + documentTitle: 'Notice of Docket Change for Docket Entry No. [Index]', + documentType: 'Notice of Docket Change', + eventCode: 'NODC', +}; + +const NOTICE_OF_TRIAL = { + documentTitle: 'Notice of Trial on [Date] at [Time]', + documentType: 'Notice of Trial', + eventCode: 'NDT', +}; + +const STANDING_PRETRIAL_NOTICE = { + documentTitle: 'Standing Pretrial Notice', + documentType: 'Standing Pretrial Notice', + eventCode: 'SPTN', +}; + +const STANDING_PRETRIAL_ORDER = { + documentTitle: 'Standing Pretrial Order', + documentType: 'Standing Pretrial Order', + eventCode: 'SPTO', +}; + +const SYSTEM_GENERATED_DOCUMENT_TYPES = { + noticeOfDocketChange: NOTICE_OF_DOCKET_CHANGE, + noticeOfTrial: NOTICE_OF_TRIAL, + standingPretrialNotice: STANDING_PRETRIAL_NOTICE, + standingPretrialOrder: STANDING_PRETRIAL_ORDER, +}; + +const SIGNED_DOCUMENT_TYPES = { + signedStipulatedDecision: { + documentType: 'Stipulated Decision', + eventCode: 'SDEC', + }, +}; + +const PRACTITIONER_ASSOCIATION_DOCUMENT_TYPES = [ + 'Entry of Appearance', + 'Substitution of Counsel', +]; + +const EVENT_CODES = [ + INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee.eventCode, + INITIAL_DOCUMENT_TYPES.ownershipDisclosure.eventCode, + INITIAL_DOCUMENT_TYPES.petition.eventCode, + INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, + INITIAL_DOCUMENT_TYPES.stin.eventCode, + NOTICE_OF_DOCKET_CHANGE.eventCode, + NOTICE_OF_TRIAL.eventCode, + STANDING_PRETRIAL_NOTICE.eventCode, + STANDING_PRETRIAL_ORDER.eventCode, + 'MISL', + 'FEE', + 'FEEW', + 'MGRTED', + 'MIND', + 'MINC', +]; + +const PAYMENT_STATUS = { + PAID: 'Paid', + UNPAID: 'Not Paid', + WAIVED: 'Waived', +}; + +const PROCEDURE_TYPES = ['Regular', 'Small']; // This is the order that they appear in the UI + +const STATUS_TYPES_WITH_ASSOCIATED_JUDGE = [ + CASE_STATUS_TYPES.assignedCase, + CASE_STATUS_TYPES.assignedMotion, + CASE_STATUS_TYPES.cav, + CASE_STATUS_TYPES.jurisdictionRetained, + CASE_STATUS_TYPES.rule155, + CASE_STATUS_TYPES.submitted, +]; + +const STATUS_TYPES_MANUAL_UPDATE = [ + CASE_STATUS_TYPES.assignedCase, + CASE_STATUS_TYPES.assignedMotion, + CASE_STATUS_TYPES.cav, + CASE_STATUS_TYPES.closed, + CASE_STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.generalDocketReadyForTrial, + CASE_STATUS_TYPES.jurisdictionRetained, + CASE_STATUS_TYPES.onAppeal, + CASE_STATUS_TYPES.rule155, + CASE_STATUS_TYPES.submitted, +]; + +const ANSWER_DOCUMENT_CODES = [ + 'A', + 'AAAP', + 'AAPN', + 'AATP', + 'AATS', + 'AATT', + 'APA', + 'ASAP', + 'ASUP', + 'ATAP', + 'ATSP', +]; + +const CASE_CAPTION_POSTFIX = 'v. Commissioner of Internal Revenue, Respondent'; + +const AUTOMATIC_BLOCKED_REASONS = { + dueDate: 'Due Date', + pending: 'Pending Item', + pendingAndDueDate: 'Pending Item and Due Date', +}; + +const CASE_TYPES_MAP = { + cdp: 'CDP (Lien/Levy)', + deficiency: 'Deficiency', + djExemptOrg: 'Declaratory Judgment (Exempt Organization)', + djRetirementPlan: 'Declaratory Judgment (Retirement Plan)', + innocentSpouse: 'Innocent Spouse', + interestAbatement: 'Interest Abatement', + other: 'Other', + partnershipSection1101: 'Partnership (BBA Section 1101)', + partnershipSection6226: 'Partnership (Section 6226)', + partnershipSection6228: 'Partnership (Section 6228)', + passport: 'Passport', + whistleblower: 'Whistleblower', + workerClassification: 'Worker Classification', +}; + +const CASE_TYPES = Object.values(CASE_TYPES_MAP); + +const ROLES = { + adc: 'adc', + admin: 'admin', + admissionsClerk: 'admissionsclerk', + chambers: 'chambers', + clerkOfCourt: 'clerkofcourt', + docketClerk: 'docketclerk', + floater: 'floater', + inactivePractitioner: 'inactivePractitioner', + irsPractitioner: 'irsPractitioner', + irsSuperuser: 'irsSuperuser', + judge: 'judge', + petitioner: 'petitioner', + petitionsClerk: 'petitionsclerk', + privatePractitioner: 'privatePractitioner', + trialClerk: 'trialclerk', +}; + +const FILING_TYPES = { + [ROLES.petitioner]: ['Myself', 'Myself and my spouse', 'A business', 'Other'], + [ROLES.privatePractitioner]: [ + 'Individual petitioner', + 'Petitioner and spouse', + 'A business', + 'Other', + ], +}; + +const ANSWER_CUTOFF_AMOUNT_IN_DAYS = 45; + +const ANSWER_CUTOFF_UNIT = 'day'; + +const COUNTRY_TYPES = { + DOMESTIC: 'domestic', + INTERNATIONAL: 'international', +}; + +const US_STATES = { + AK: 'Alaska', + AL: 'Alabama', + AR: 'Arkansas', + AZ: 'Arizona', + CA: 'California', + CO: 'Colorado', + CT: 'Connecticut', + DC: 'District of Columbia', + DE: 'Delaware', + FL: 'Florida', + GA: 'Georgia', + HI: 'Hawaii', + IA: 'Iowa', + ID: 'Idaho', + IL: 'Illinois', + IN: 'Indiana', + KS: 'Kansas', + KY: 'Kentucky', + LA: 'Louisiana', + MA: 'Massachusetts', + MD: 'Maryland', + ME: 'Maine', + MI: 'Michigan', + MN: 'Minnesota', + MO: 'Missouri', + MS: 'Mississippi', + MT: 'Montana', + NC: 'North Carolina', + ND: 'North Dakota', + NE: 'Nebraska', + NH: 'New Hampshire', + NJ: 'New Jersey', + NM: 'New Mexico', + NV: 'Nevada', + NY: 'New York', + OH: 'Ohio', + OK: 'Oklahoma', + OR: 'Oregon', + PA: 'Pennsylvania', + RI: 'Rhode Island', + SC: 'South Carolina', + SD: 'South Dakota', + TN: 'Tennessee', + TX: 'Texas', + UT: 'Utah', + VA: 'Virginia', + VT: 'Vermont', + WA: 'Washington', + WI: 'Wisconsin', + WV: 'West Virginia', + WY: 'Wyoming', +}; + +const PARTY_TYPES = { + conservator: 'Conservator', + corporation: 'Corporation', + custodian: 'Custodian', + donor: 'Donor', + estate: 'Estate with an executor/personal representative/fiduciary/etc.', + estateWithoutExecutor: + 'Estate without an executor/personal representative/fiduciary/etc.', + guardian: 'Guardian', + nextFriendForIncompetentPerson: + 'Next friend for a legally incompetent person (without a guardian, conservator, or other like fiduciary)', + nextFriendForMinor: + 'Next friend for a minor (without a guardian, conservator, or other like fiduciary)', + partnershipAsTaxMattersPartner: 'Partnership (as the Tax Matters Partner)', + partnershipBBA: + 'Partnership (as a partnership representative under the BBA regime)', + partnershipOtherThanTaxMatters: + 'Partnership (as a partner other than Tax Matters Partner)', + petitioner: 'Petitioner', + petitionerDeceasedSpouse: 'Petitioner & deceased spouse', + petitionerSpouse: 'Petitioner & spouse', + survivingSpouse: 'Surviving spouse', + transferee: 'Transferee', + trust: 'Trust', +}; + +const BUSINESS_TYPES = { + corporation: PARTY_TYPES.corporation, + partnershipAsTaxMattersPartner: PARTY_TYPES.partnershipAsTaxMattersPartner, + partnershipBBA: PARTY_TYPES.partnershipBBA, + partnershipOtherThanTaxMatters: PARTY_TYPES.partnershipOtherThanTaxMatters, +}; + +const ESTATE_TYPES = { + estate: PARTY_TYPES.estate, + estateWithoutExecutor: PARTY_TYPES.estateWithoutExecutor, + trust: PARTY_TYPES.trust, +}; + +const OTHER_TYPES = { + conservator: PARTY_TYPES.conservator, + custodian: PARTY_TYPES.custodian, + guardian: PARTY_TYPES.guardian, + nextFriendForIncompetentPerson: PARTY_TYPES.nextFriendForIncompetentPerson, + nextFriendForMinor: PARTY_TYPES.nextFriendForMinor, +}; + +const ORDER_TYPES = [ + { + documentType: 'Order', + eventCode: 'O', + }, + { + documentTitle: 'Order of Dismissal for Lack of Jurisdiction', + documentType: 'Order of Dismissal for Lack of Jurisdiction', + eventCode: 'ODJ', + }, + { + documentTitle: 'Order of Dismissal', + documentType: 'Order of Dismissal', + eventCode: 'OD', + }, + { + documentTitle: 'Order of Dismissal and Decision', + documentType: 'Order of Dismissal and Decision', + eventCode: 'ODD', + }, + { + documentTitle: 'Order to Show Cause', + documentType: 'Order to Show Cause', + eventCode: 'OSC', + }, + { + documentTitle: 'Order and Decision', + documentType: 'Order and Decision', + eventCode: 'OAD', + }, + { + documentTitle: 'Decision', + documentType: 'Decision', + eventCode: 'DEC', + }, + { + documentType: 'Notice', + eventCode: 'NOT', + }, +]; + +const COMMON_CITIES = [ + { city: 'Birmingham', state: 'Alabama' }, + { city: 'Mobile', state: 'Alabama' }, + { city: 'Anchorage', state: 'Alaska' }, + { city: 'Phoenix', state: 'Arizona' }, + { city: 'Little Rock', state: 'Arkansas' }, + { city: 'Los Angeles', state: 'California' }, + { city: 'San Diego', state: 'California' }, + { city: 'San Francisco', state: 'California' }, + { city: 'Denver', state: 'Colorado' }, + { city: 'Hartford', state: 'Connecticut' }, + { city: 'Washington', state: 'District of Columbia' }, + { city: 'Jacksonville', state: 'Florida' }, + { city: 'Miami', state: 'Florida' }, + { city: 'Tampa', state: 'Florida' }, + { city: 'Atlanta', state: 'Georgia' }, + { city: 'Honolulu', state: 'Hawaii' }, + { city: 'Boise', state: 'Idaho' }, + { city: 'Chicago', state: 'Illinois' }, + { city: 'Indianapolis', state: 'Indiana' }, + { city: 'Des Moines', state: 'Iowa' }, + { city: 'Louisville', state: 'Kentucky' }, + { city: 'New Orleans', state: 'Louisiana' }, + { city: 'Baltimore', state: 'Maryland' }, + { city: 'Boston', state: 'Massachusetts' }, + { city: 'Detroit', state: 'Michigan' }, + { city: 'St. Paul', state: 'Minnesota' }, + { city: 'Jackson', state: 'Mississippi' }, + { city: 'Kansas City', state: 'Missouri' }, + { city: 'St. Louis', state: 'Missouri' }, + { city: 'Helena', state: 'Montana' }, + { city: 'Omaha', state: 'Nebraska' }, + { city: 'Las Vegas', state: 'Nevada' }, + { city: 'Reno', state: 'Nevada' }, + { city: 'Albuquerque', state: 'New Mexico' }, + { city: 'Buffalo', state: 'New York' }, + { city: 'New York City', state: 'New York' }, + { city: 'Winston-Salem', state: 'North Carolina' }, + { city: 'Cincinnati', state: 'Ohio' }, + { city: 'Cleveland', state: 'Ohio' }, + { city: 'Columbus', state: 'Ohio' }, + { city: 'Oklahoma City', state: 'Oklahoma' }, + { city: 'Portland', state: 'Oregon' }, + { city: 'Philadelphia', state: 'Pennsylvania' }, + { city: 'Pittsburgh', state: 'Pennsylvania' }, + { city: 'Columbia', state: 'South Carolina' }, + { city: 'Knoxville', state: 'Tennessee' }, + { city: 'Memphis', state: 'Tennessee' }, + { city: 'Nashville', state: 'Tennessee' }, + { city: 'Dallas', state: 'Texas' }, + { city: 'El Paso', state: 'Texas' }, + { city: 'Houston', state: 'Texas' }, + { city: 'Lubbock', state: 'Texas' }, + { city: 'San Antonio', state: 'Texas' }, + { city: 'Salt Lake City', state: 'Utah' }, + { city: 'Richmond', state: 'Virginia' }, + { city: 'Seattle', state: 'Washington' }, + { city: 'Spokane', state: 'Washington' }, + { city: 'Charleston', state: 'West Virginia' }, + { city: 'Milwaukee', state: 'Wisconsin' }, +]; + +const SMALL_CITIES = [ + { city: 'Fresno', state: 'California' }, + { city: 'Tallahassee', state: 'Florida' }, + { city: 'Pocatello', state: 'Idaho' }, + { city: 'Peoria', state: 'Illinois' }, + { city: 'Wichita', state: 'Kansas' }, + { city: 'Shreveport', state: 'Louisiana' }, + { city: 'Portland', state: 'Maine' }, + { city: 'Billings', state: 'Montana' }, + { city: 'Albany', state: 'New York' }, + { city: 'Syracuse', state: 'New York' }, + { city: 'Bismarck', state: 'North Dakota' }, + { city: 'Aberdeen', state: 'South Dakota' }, + { city: 'Burlington', state: 'Vermont' }, + { city: 'Roanoke', state: 'Virginia' }, + { city: 'Cheyenne', state: 'Wyoming' }, + ...COMMON_CITIES, +]; + +const TRIAL_CITIES = { + ALL: SMALL_CITIES, + REGULAR: COMMON_CITIES, + SMALL: SMALL_CITIES, +}; + +const TRIAL_CITY_STRINGS = SMALL_CITIES.map( + location => `${location.city}, ${location.state}`, +); + +const SESSION_TERMS = ['Winter', 'Fall', 'Spring', 'Summer']; + +const SESSION_TYPES = [ + 'Regular', + 'Small', + 'Hybrid', + 'Special', + 'Motion/Hearing', +]; + +const SESSION_STATUS_GROUPS = { + all: 'All', + closed: 'Closed', + new: 'New', + open: 'Open', +}; + +const MAX_FILE_SIZE_MB = 250; // megabytes +const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024; // bytes -> megabytes + +const ADC_SECTION = 'adc'; +const ADMISSIONS_SECTION = 'admissions'; +const CHAMBERS_SECTION = 'chambers'; +const CLERK_OF_COURT_SECTION = 'clerkofcourt'; +const DOCKET_SECTION = 'docket'; +const IRS_SYSTEM_SECTION = 'irsSystem'; +const PETITIONS_SECTION = 'petitions'; +const TRIAL_CLERKS_SECTION = 'trialClerks'; + +const ARMENS_CHAMBERS_SECTION = 'armensChambers'; +const ASHFORDS_CHAMBERS_SECTION = 'ashfordsChambers'; +const BUCHS_CHAMBERS_SECTION = 'buchsChambers'; +const CARLUZZOS_CHAMBERS_SECTION = 'carluzzosChambers'; +const COHENS_CHAMBERS_SECTION = 'cohensChambers'; +const COLVINS_CHAMBERS_SECTION = 'colvinsChambers'; +const COPELANDS_CHAMBERS_SECTION = 'copelandsChambers'; +const FOLEYS_CHAMBERS_SECTION = 'foleysChambers'; +const GALES_CHAMBERS_SECTION = 'galesChambers'; +const GERBERS_CHAMBERS_SECTION = 'gerbersChambers'; +const GOEKES_CHAMBERS_SECTION = 'goekesChambers'; +const GUSTAFSONS_CHAMBERS_SECTION = 'gustafsonsChambers'; +const GUYS_CHAMBERS_SECTION = 'guysChambers'; +const HALPERNS_CHAMBERS_SECTION = 'halpernsChambers'; +const HOLMES_CHAMBERS_SECTION = 'holmesChambers'; +const JACOBS_CHAMBERS_SECTION = 'jacobsChambers'; +const JONES_CHAMBERS_SECTION = 'jonesChambers'; +const KERRIGANS_CHAMBERS_SECTION = 'kerrigansChambers'; +const LAUBERS_CHAMBERS_SECTION = 'laubersChambers'; +const LEYDENS_CHAMBERS_SECTION = 'leydensChambers'; +const MARVELS_CHAMBERS_SECTION = 'marvelsChambers'; +const MORRISONS_CHAMBERS_SECTION = 'morrisonsChambers'; +const NEGAS_CHAMBERS_SECTION = 'negasChambers'; +const PANUTHOS_CHAMBERS_SECTION = 'panuthosChambers'; +const PARIS_CHAMBERS_SECTION = 'parisChambers'; +const PUGHS_CHAMBERS_SECTION = 'pughsChambers'; +const RUWES_CHAMBERS_SECTION = 'ruwesChambers'; +const THORNTONS_CHAMBERS_SECTION = 'thorntonsChambers'; +const TOROS_CHAMBERS_SECTION = 'torosChambers'; +const URDAS_CHAMBERS_SECTION = 'urdasChambers'; +const VASQUEZS_CHAMBERS_SECTION = 'vasquezsChambers'; +const WELLS_CHAMBERS_SECTION = 'wellsChambers'; + +const SECTIONS = sortBy([ + ADC_SECTION, + ADMISSIONS_SECTION, + CHAMBERS_SECTION, + CLERK_OF_COURT_SECTION, + DOCKET_SECTION, + PETITIONS_SECTION, + TRIAL_CLERKS_SECTION, +]); + +const CHAMBERS_SECTIONS = sortBy([ + ARMENS_CHAMBERS_SECTION, + ASHFORDS_CHAMBERS_SECTION, + BUCHS_CHAMBERS_SECTION, + CARLUZZOS_CHAMBERS_SECTION, + COHENS_CHAMBERS_SECTION, + COLVINS_CHAMBERS_SECTION, + COPELANDS_CHAMBERS_SECTION, + FOLEYS_CHAMBERS_SECTION, + GALES_CHAMBERS_SECTION, + GERBERS_CHAMBERS_SECTION, + GOEKES_CHAMBERS_SECTION, + GUSTAFSONS_CHAMBERS_SECTION, + GUYS_CHAMBERS_SECTION, + HALPERNS_CHAMBERS_SECTION, + HOLMES_CHAMBERS_SECTION, + JACOBS_CHAMBERS_SECTION, + JONES_CHAMBERS_SECTION, + KERRIGANS_CHAMBERS_SECTION, + LAUBERS_CHAMBERS_SECTION, + LEYDENS_CHAMBERS_SECTION, + MARVELS_CHAMBERS_SECTION, + MORRISONS_CHAMBERS_SECTION, + NEGAS_CHAMBERS_SECTION, + PANUTHOS_CHAMBERS_SECTION, + PARIS_CHAMBERS_SECTION, + PUGHS_CHAMBERS_SECTION, + RUWES_CHAMBERS_SECTION, + THORNTONS_CHAMBERS_SECTION, + URDAS_CHAMBERS_SECTION, + TOROS_CHAMBERS_SECTION, + VASQUEZS_CHAMBERS_SECTION, + WELLS_CHAMBERS_SECTION, +]); + +const TRIAL_STATUS_TYPES = [ + 'Set for Trial', + 'Dismissed', + 'Continued', + 'Rule 122', + 'A Basis Reached', + 'Settled', + 'Recall', + 'Taken Under Advisement', +]; + +const SCAN_MODES = { + DUPLEX: 'duplex', + FEEDER: 'feeder', + FLATBED: 'flatbed', +}; + +const EMPLOYER_OPTIONS = ['IRS', 'DOJ', 'Private']; + +const PRACTITIONER_TYPE_OPTIONS = ['Attorney', 'Non-Attorney']; + +const ADMISSIONS_STATUS_OPTIONS = [ + 'Active', + 'Suspended', + 'Disbarred', + 'Resigned', + 'Deceased', + 'Inactive', +]; + +const DEFAULT_PROCEDURE_TYPE = PROCEDURE_TYPES[0]; + +const CASE_SEARCH_MIN_YEAR = 1986; +const CASE_SEARCH_PAGE_SIZE = 5; + +module.exports = { + ADC_SECTION, + ADMISSIONS_SECTION, + ADMISSIONS_STATUS_OPTIONS, + ANSWER_CUTOFF_AMOUNT_IN_DAYS, + ANSWER_CUTOFF_UNIT, + ANSWER_DOCUMENT_CODES, + AUTOMATIC_BLOCKED_REASONS, + BUSINESS_TYPES, + CASE_CAPTION_POSTFIX, + CASE_SEARCH_MIN_YEAR, + CASE_SEARCH_PAGE_SIZE, + CASE_STATUS_TYPES, + CASE_TYPES, + CASE_TYPES_MAP, + CHAMBERS_SECTION, + CHAMBERS_SECTIONS, + CHIEF_JUDGE, + CLERK_OF_COURT_SECTION, + CONTACT_CHANGE_DOCUMENT_TYPES, + COUNTRY_TYPES, + COURT_ISSUED_EVENT_CODES, + DEFAULT_PROCEDURE_TYPE, + DOCKET_NUMBER_MATCHER, + DOCKET_NUMBER_SUFFIXES, + DOCKET_SECTION, + DOCUMENT_CATEGORIES, + DOCUMENT_CATEGORY_MAP, + DOCUMENT_INTERNAL_CATEGORIES, + DOCUMENT_INTERNAL_CATEGORY_MAP, + DOCUMENT_NOTICE_EVENT_CODES, + DOCUMENT_RELATIONSHIPS, + EMPLOYER_OPTIONS, + ESTATE_TYPES, + EVENT_CODES, + FILING_TYPES, + INITIAL_DOCUMENT_TYPES, + IRS_SYSTEM_SECTION, + MAX_FILE_SIZE_BYTES, + MAX_FILE_SIZE_MB, + NOTICE_OF_DOCKET_CHANGE, + NOTICE_OF_TRIAL, + OBJECTIONS_OPTIONS, + OPINION_DOCUMENT_TYPES, + ORDER_DOCUMENT_TYPES, + ORDER_TYPES, + OTHER_TYPES, + PARTY_TYPES, + PAYMENT_STATUS, + PETITIONS_SECTION, + PRACTITIONER_ASSOCIATION_DOCUMENT_TYPES, + PRACTITIONER_TYPE_OPTIONS, + PROCEDURE_TYPES, + ROLES, + SCAN_MODES, + SCENARIOS, + SECTIONS, + SERVICE_INDICATOR_TYPES, + SESSION_STATUS_GROUPS, + SESSION_TERMS, + SESSION_TYPES, + SIGNED_DOCUMENT_TYPES, + STANDING_PRETRIAL_NOTICE, + STANDING_PRETRIAL_ORDER, + STATUS_TYPES_MANUAL_UPDATE, + STATUS_TYPES_WITH_ASSOCIATED_JUDGE, + SYSTEM_GENERATED_DOCUMENT_TYPES, + TRACKED_DOCUMENT_TYPES, + TRANSCRIPT_EVENT_CODE, + TRIAL_CITIES, + TRIAL_CITY_STRINGS, + TRIAL_CLERKS_SECTION, + TRIAL_LOCATION_MATCHER, + TRIAL_STATUS_TYPES, + US_STATES, +}; diff --git a/shared/src/business/entities/ForwardMessage.js b/shared/src/business/entities/ForwardMessage.js index 3149e24ecaf..676da8fd3ab 100644 --- a/shared/src/business/entities/ForwardMessage.js +++ b/shared/src/business/entities/ForwardMessage.js @@ -2,6 +2,7 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); +const { CHAMBERS_SECTIONS, SECTIONS } = require('./EntityConstants'); ForwardMessage.VALIDATION_ERROR_MESSAGES = { assigneeId: 'Select a recipient', @@ -29,8 +30,11 @@ joiValidationDecorator( version: ['uuidv4'], }) .required(), - forwardMessage: joi.string().required(), - section: joi.string().required(), + forwardMessage: joi.string().max(500).required(), + section: joi + .string() + .valid(...SECTIONS, ...CHAMBERS_SECTIONS) + .required(), }), ForwardMessage.VALIDATION_ERROR_MESSAGES, ); diff --git a/shared/src/business/entities/ForwardMessage.test.js b/shared/src/business/entities/ForwardMessage.test.js index 3ec74f6922f..3ff523402ef 100644 --- a/shared/src/business/entities/ForwardMessage.test.js +++ b/shared/src/business/entities/ForwardMessage.test.js @@ -18,7 +18,7 @@ describe('ForwardMessage', () => { assigneeId: '1805d1ab-18d0-43ec-bafb-654e83405416', forwardMessage: 'If everyone is moving forward together, then success takes care of itself.', - section: 'HenryFord', + section: 'petitions', }); expect(entity.getFormattedValidationErrors()).toEqual(null); }); diff --git a/shared/src/business/entities/InitialWorkItemMessage.js b/shared/src/business/entities/InitialWorkItemMessage.js index d3b34fa853b..27395e73468 100644 --- a/shared/src/business/entities/InitialWorkItemMessage.js +++ b/shared/src/business/entities/InitialWorkItemMessage.js @@ -2,6 +2,7 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); +const { CHAMBERS_SECTIONS, SECTIONS } = require('./EntityConstants'); InitialWorkItemMessage.VALIDATION_ERROR_MESSAGES = { assigneeId: 'Select a recipient', @@ -29,8 +30,11 @@ joiValidationDecorator( version: ['uuidv4'], }) .required(), - message: joi.string().required(), - section: joi.string().required(), + message: joi.string().max(500).required(), + section: joi + .string() + .valid(...SECTIONS, ...CHAMBERS_SECTIONS) + .required(), }), InitialWorkItemMessage.VALIDATION_ERROR_MESSAGES, ); diff --git a/shared/src/business/entities/IrsPractitioner.js b/shared/src/business/entities/IrsPractitioner.js index a7a563bba83..f8e1c9c22a3 100644 --- a/shared/src/business/entities/IrsPractitioner.js +++ b/shared/src/business/entities/IrsPractitioner.js @@ -3,12 +3,11 @@ const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); const { - User, userDecorator, userValidation, VALIDATION_ERROR_MESSAGES, } = require('./User'); -const { SERVICE_INDICATOR_TYPES } = require('./cases/CaseConstants'); +const { ROLES, SERVICE_INDICATOR_TYPES } = require('./EntityConstants'); /** * constructor @@ -28,7 +27,7 @@ joiValidationDecorator( joi.object().keys({ ...userValidation, entityName: joi.string().valid('IrsPractitioner').required(), - role: joi.string().valid(User.ROLES.irsPractitioner).required(), + role: joi.string().valid(ROLES.irsPractitioner).required(), serviceIndicator: joi .string() .valid(...Object.values(SERVICE_INDICATOR_TYPES)) diff --git a/shared/src/business/entities/IrsPractitioner.test.js b/shared/src/business/entities/IrsPractitioner.test.js index 1217fab12bb..1a3efe3c1ae 100644 --- a/shared/src/business/entities/IrsPractitioner.test.js +++ b/shared/src/business/entities/IrsPractitioner.test.js @@ -1,5 +1,5 @@ const { IrsPractitioner } = require('./IrsPractitioner'); -const { User } = require('./User'); +const { ROLES } = require('./EntityConstants'); describe('IrsPractitioner', () => { it('Creates a valid IrsPractitioner', () => { @@ -17,8 +17,8 @@ describe('IrsPractitioner', () => { }, firstName: 'firstName', lastName: 'lastName', - role: User.ROLES.irsPractitioner, - userId: 'petitioner', + role: ROLES.irsPractitioner, + userId: '9ea9732c-9751-4159-9619-bd27556eb9bc', }); expect(user.isValid()).toBeTruthy(); @@ -27,7 +27,7 @@ describe('IrsPractitioner', () => { it('Creates an invalid', () => { const user = new IrsPractitioner({ - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, }); expect(user.isValid()).toBeFalsy(); diff --git a/shared/src/business/entities/Message.js b/shared/src/business/entities/Message.js index bae170fdbb3..b5ffc5e0430 100644 --- a/shared/src/business/entities/Message.js +++ b/shared/src/business/entities/Message.js @@ -33,21 +33,21 @@ joiValidationDecorator( joi.object().keys({ createdAt: joiStrictTimestamp.optional(), entityName: joi.string().valid('Message').required(), - from: joi.string().required(), + from: joi.string().max(100).required(), fromUserId: joi .string() .uuid({ version: ['uuidv4'], }) .required(), - message: joi.string().required(), + message: joi.string().max(500).required(), messageId: joi .string() .uuid({ version: ['uuidv4'], }) .required(), - to: joi.string().optional().allow(null), + to: joi.string().max(100).optional().allow(null), toUserId: joi .string() .uuid({ diff --git a/shared/src/business/entities/NewCaseMessage.js b/shared/src/business/entities/NewCaseMessage.js new file mode 100644 index 00000000000..4ad32fbb12f --- /dev/null +++ b/shared/src/business/entities/NewCaseMessage.js @@ -0,0 +1,47 @@ +const joi = require('@hapi/joi'); +const { + joiValidationDecorator, +} = require('../../utilities/JoiValidationDecorator'); +const { CaseMessage } = require('./CaseMessage'); + +/** + * NewCaseMessage entity - used for validating + * the Create Case Message modal form + * + * @param {object} rawMessage the raw message data + * @constructor + */ +function NewCaseMessage(rawMessage, { applicationContext }) { + if (!applicationContext) { + throw new TypeError('applicationContext must be defined'); + } + this.entityName = 'NewCaseMessage'; + + this.message = rawMessage.message; + this.subject = rawMessage.subject; + this.toSection = rawMessage.toSection; + this.toUserId = rawMessage.toUserId; +} + +NewCaseMessage.validationName = 'NewCaseMessage'; + +NewCaseMessage.VALIDATION_ERROR_MESSAGES = { + message: 'Enter a message', + subject: 'Enter a subject line', + toSection: 'Select a section', + toUserId: 'Select a recipient', +}; + +joiValidationDecorator( + NewCaseMessage, + joi.object().keys({ + entityName: joi.string().valid('NewCaseMessage').required(), + message: CaseMessage.VALIDATION_RULES.message, + subject: CaseMessage.VALIDATION_RULES.subject, + toSection: CaseMessage.VALIDATION_RULES.toSection, + toUserId: CaseMessage.VALIDATION_RULES.toUserId, + }), + NewCaseMessage.VALIDATION_ERROR_MESSAGES, +); + +module.exports = { NewCaseMessage }; diff --git a/shared/src/business/entities/NewCaseMessage.test.js b/shared/src/business/entities/NewCaseMessage.test.js new file mode 100644 index 00000000000..83eeda5f2ed --- /dev/null +++ b/shared/src/business/entities/NewCaseMessage.test.js @@ -0,0 +1,37 @@ +const { applicationContext } = require('../test/createTestApplicationContext'); +const { NewCaseMessage } = require('./NewCaseMessage'); + +describe('NewCaseMessage', () => { + describe('isValid', () => { + it('should throw an error if app context is not passed in', () => { + expect(() => new NewCaseMessage({}, {})).toThrow(); + }); + + it('creates a valid NewCaseMessage', () => { + const message = new NewCaseMessage( + { + message: 'hello world', + subject: 'hey!', + to: 'bob', + toSection: 'petitions', + toUserId: '6805d1ab-18d0-43ec-bafb-654e83405416', + }, + { applicationContext }, + ); + expect(message.isValid()).toBeTruthy(); + }); + + it('creates an invalid NewCaseMessage with no message', () => { + const message = new NewCaseMessage( + { + subject: 'hey!', + to: 'bob', + toSection: 'petitions', + toUserId: '6805d1ab-18d0-43ec-bafb-654e83405416', + }, + { applicationContext }, + ); + expect(message.isValid()).toBeFalsy(); + }); + }); +}); diff --git a/shared/src/business/entities/NewPractitioner.test.js b/shared/src/business/entities/NewPractitioner.test.js index 7d3f0e7bc09..188c6993632 100644 --- a/shared/src/business/entities/NewPractitioner.test.js +++ b/shared/src/business/entities/NewPractitioner.test.js @@ -1,5 +1,5 @@ const { NewPractitioner } = require('./NewPractitioner'); -const { User } = require('./User'); +const { ROLES } = require('./EntityConstants'); describe('NewPractitioner', () => { it('Creates a valid NewPractitioner with all required fields', () => { @@ -25,14 +25,14 @@ describe('NewPractitioner', () => { lastName: 'Practitioner', originalBarState: 'Illinois', practitionerType: 'Attorney', - role: User.ROLES.NewPractitioner, + role: ROLES.NewPractitioner, }); expect(user.isValid()).toBeTruthy(); }); it('Creates an invalid NewPractitioner with missing required fields', () => { const user = new NewPractitioner({ - role: User.ROLES.NewPractitioner, + role: ROLES.NewPractitioner, }); expect(user.isValid()).toBeFalsy(); }); @@ -58,7 +58,7 @@ describe('NewPractitioner', () => { firmName: 'GW Law Offices', originalBarState: 'Illinois', practitionerType: 'Attorney', - role: User.ROLES.NewPractitioner, + role: ROLES.NewPractitioner, }); expect(user.isValid()).toBeFalsy(); }); diff --git a/shared/src/business/entities/Practitioner.js b/shared/src/business/entities/Practitioner.js index 46aefade3bd..e94989e23da 100644 --- a/shared/src/business/entities/Practitioner.js +++ b/shared/src/business/entities/Practitioner.js @@ -1,25 +1,20 @@ const joi = require('@hapi/joi'); +const { + ADMISSIONS_STATUS_OPTIONS, + EMPLOYER_OPTIONS, + PRACTITIONER_TYPE_OPTIONS, + ROLES, +} = require('./EntityConstants'); const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); const { - User, userDecorator, userValidation, VALIDATION_ERROR_MESSAGES: USER_VALIDATION_ERROR_MESSAGES, } = require('./User'); const { getTimestampSchema } = require('../../utilities/dateSchema'); const joiStrictTimestamp = getTimestampSchema(); -const EMPLOYER_OPTIONS = ['IRS', 'DOJ', 'Private']; -const PRACTITIONER_TYPE_OPTIONS = ['Attorney', 'Non-Attorney']; -const ADMISSIONS_STATUS_OPTIONS = [ - 'Active', - 'Suspended', - 'Disbarred', - 'Resigned', - 'Deceased', - 'Inactive', -]; /** * constructor @@ -32,9 +27,9 @@ function Practitioner(rawUser) { } const roleMap = { - DOJ: User.ROLES.irsPractitioner, - IRS: User.ROLES.irsPractitioner, - Private: User.ROLES.privatePractitioner, + DOJ: ROLES.irsPractitioner, + IRS: ROLES.irsPractitioner, + Private: ROLES.privatePractitioner, }; Practitioner.prototype.init = function (rawUser) { @@ -56,7 +51,7 @@ Practitioner.prototype.init = function (rawUser) { if (this.admissionsStatus === 'Active') { this.role = roleMap[this.employer]; } else { - this.role = User.ROLES.inactivePractitioner; + this.role = ROLES.inactivePractitioner; } this.suffix = rawUser.suffix; this.section = this.role; @@ -89,6 +84,7 @@ const practitionerValidation = { ...userValidation, additionalPhone: joi .string() + .max(100) .optional() .allow(null) .description('An alternate phone number for the practitioner.'), @@ -105,11 +101,13 @@ const practitionerValidation = { .description('The Tax Court bar admission status for the practitioner.'), alternateEmail: joi .string() + .max(100) .optional() .allow(null) .description('An alternate email address for the practitioner.'), barNumber: joi .string() + .max(100) .required() .description( 'A unique identifier comprising of the practitioner initials, date, and series number.', @@ -129,24 +127,29 @@ const practitionerValidation = { entityName: joi.string().valid('Practitioner').required(), firmName: joi .string() + .max(100) .optional() .allow(null) .description('The firm name for the practitioner.'), firstName: joi .string() + .max(100) .required() .description('The first name of the practitioner.'), lastName: joi .string() + .max(100) .required() .description('The last name of the practitioner.'), middleName: joi .string() + .max(100) .optional() .allow(null) .description('The optional middle name of the practitioner.'), originalBarState: joi .string() + .max(100) .required() .description( 'The state in which the practitioner passed their bar examination.', @@ -158,14 +161,15 @@ const practitionerValidation = { .description('The type of practitioner - either Attorney or Non-Attorney.'), role: joi.alternatives().conditional('admissionsStatus', { is: joi.valid('Active'), - otherwise: joi.string().valid(User.ROLES.inactivePractitioner).required(), + otherwise: joi.string().valid(ROLES.inactivePractitioner).required(), then: joi .string() - .valid(...[User.ROLES.irsPractitioner, User.ROLES.privatePractitioner]) + .valid(...[ROLES.irsPractitioner, ROLES.privatePractitioner]) .required(), }), suffix: joi .string() + .max(100) .optional() .allow('') .description('The name suffix of the practitioner.'), @@ -180,12 +184,8 @@ joiValidationDecorator( ); Practitioner.validationName = 'Practitioner'; - -Practitioner.PRACTITIONER_TYPE_OPTIONS = PRACTITIONER_TYPE_OPTIONS; -Practitioner.EMPLOYER_OPTIONS = EMPLOYER_OPTIONS; Practitioner.validationRules = practitionerValidation; Practitioner.VALIDATION_ERROR_MESSAGES = VALIDATION_ERROR_MESSAGES; -Practitioner.ADMISSIONS_STATUS_OPTIONS = ADMISSIONS_STATUS_OPTIONS; /** * returns the full concatenated name for the given practitioner data diff --git a/shared/src/business/entities/Practitioner.test.js b/shared/src/business/entities/Practitioner.test.js index 272a9b9acbc..4bea0688b0d 100644 --- a/shared/src/business/entities/Practitioner.test.js +++ b/shared/src/business/entities/Practitioner.test.js @@ -1,5 +1,5 @@ const { Practitioner } = require('./Practitioner'); -const { User } = require('./User'); +const { ROLES } = require('./EntityConstants'); describe('Practitioner', () => { it('Creates a valid Practitioner with all required fields', () => { @@ -26,15 +26,15 @@ describe('Practitioner', () => { name: 'Test Practitioner', originalBarState: 'Illinois', practitionerType: 'Attorney', - role: User.ROLES.Practitioner, - userId: 'practitioner', + role: ROLES.Practitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); }); it('Creates an invalid Practitioner with missing required fields', () => { const user = new Practitioner({ - role: User.ROLES.Practitioner, + role: ROLES.Practitioner, }); expect(user.isValid()).toBeFalsy(); }); @@ -61,8 +61,8 @@ describe('Practitioner', () => { lastName: 'Practitioner', name: 'Test Practitioner', practitionerType: 'Attorney', - role: User.ROLES.Practitioner, - userId: 'practitioner', + role: ROLES.Practitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeFalsy(); }); @@ -90,8 +90,8 @@ describe('Practitioner', () => { lastName: 'Practitioner', name: 'Test Practitioner', practitionerType: 'Purple', - role: User.ROLES.Practitioner, - userId: 'practitioner', + role: ROLES.Practitioner, + userId: 'ec4fe2e7-52cf-4084-84de-d8e8d151e927', }); expect(user.isValid()).toBeFalsy(); }); @@ -119,8 +119,8 @@ describe('Practitioner', () => { lastName: 'Practitioner', name: 'Test Practitioner', practitionerType: 'Purple', - role: User.ROLES.Practitioner, - userId: 'practitioner', + role: ROLES.Practitioner, + userId: 'ec4fe2e7-52cf-4084-84de-d8e8d151e927', }); expect(user.isValid()).toBeFalsy(); }); @@ -129,7 +129,7 @@ describe('Practitioner', () => { const user = new Practitioner({ admissionsStatus: 'Active', employer: 'IRS', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, }); expect(user.isValid()).toBeFalsy(); @@ -159,8 +159,8 @@ describe('Practitioner', () => { name: 'Test Practitioner', originalBarState: 'Illinois', practitionerType: 'Attorney', - role: User.ROLES.inactivePractitioner, - userId: 'practitioner', + role: ROLES.inactivePractitioner, + userId: 'ec4fe2e7-52cf-4084-84de-d8e8d151e927', }); expect(user.isValid()).toBeTruthy(); @@ -190,8 +190,8 @@ describe('Practitioner', () => { name: 'Test Practitioner', originalBarState: 'Illinois', practitionerType: 'Attorney', - role: User.ROLES.privatePractitioner, - userId: 'practitioner', + role: ROLES.privatePractitioner, + userId: 'ec4fe2e7-52cf-4084-84de-d8e8d151e927', }); expect(user.isValid()).toBeTruthy(); @@ -202,7 +202,7 @@ describe('Practitioner', () => { admissionsStatus: 'Active', employer: 'IRS', }); - expect(user.role).toEqual(User.ROLES.irsPractitioner); + expect(user.role).toEqual(ROLES.irsPractitioner); }); it('should set the role to "irsPractitioner" when employer is "DOJ" and admissionsStatus is Active', () => { @@ -210,7 +210,7 @@ describe('Practitioner', () => { admissionsStatus: 'Active', employer: 'DOJ', }); - expect(user.role).toEqual(User.ROLES.irsPractitioner); + expect(user.role).toEqual(ROLES.irsPractitioner); }); it('should set the role to "privatePractitioner" when employer is "Private" and admissionsStatus is Active', () => { @@ -218,7 +218,7 @@ describe('Practitioner', () => { admissionsStatus: 'Active', employer: 'Private', }); - expect(user.role).toEqual(User.ROLES.privatePractitioner); + expect(user.role).toEqual(ROLES.privatePractitioner); }); it('should set the role to "inactivePractitioner" when employer is "Private" and admissionsStatus is Inactive', () => { @@ -226,7 +226,7 @@ describe('Practitioner', () => { admissionsStatus: 'Inactive', employer: 'Private', }); - expect(user.role).toEqual(User.ROLES.inactivePractitioner); + expect(user.role).toEqual(ROLES.inactivePractitioner); }); it('Combines firstName, middleName, lastName, and suffix properties to set the name property', () => { @@ -253,9 +253,9 @@ describe('Practitioner', () => { middleName: 'Middle', originalBarState: 'Illinois', practitionerType: 'Attorney', - role: User.ROLES.Practitioner, + role: ROLES.Practitioner, suffix: 'Sfx', - userId: 'practitioner', + userId: 'ec4fe2e7-52cf-4084-84de-d8e8d151e927', }); expect(user.name).toEqual('Test Middle Practitioner Sfx'); }); @@ -286,8 +286,8 @@ describe('Practitioner', () => { lastName: 'Practitioner', originalBarState: 'Illinois', practitionerType: 'Attorney', - role: User.ROLES.Practitioner, - userId: 'practitioner', + role: ROLES.Practitioner, + userId: 'ec4fe2e7-52cf-4084-84de-d8e8d151e927', }; }); diff --git a/shared/src/business/entities/PrivatePractitioner.js b/shared/src/business/entities/PrivatePractitioner.js index 7b40736f8b3..280c6b04ace 100644 --- a/shared/src/business/entities/PrivatePractitioner.js +++ b/shared/src/business/entities/PrivatePractitioner.js @@ -2,8 +2,9 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); -const { SERVICE_INDICATOR_TYPES } = require('./cases/CaseConstants'); -const { User, userDecorator, userValidation } = require('./User'); +const { ROLES } = require('./EntityConstants'); +const { SERVICE_INDICATOR_TYPES } = require('./EntityConstants'); +const { userDecorator, userValidation } = require('./User'); /** * constructor @@ -27,7 +28,7 @@ joiValidationDecorator( entityName: joi.string().valid('PrivatePractitioner').required(), representingPrimary: joi.boolean().optional(), representingSecondary: joi.boolean().optional(), - role: joi.string().required().valid(User.ROLES.privatePractitioner), + role: joi.string().required().valid(ROLES.privatePractitioner), serviceIndicator: joi .string() .valid(...Object.values(SERVICE_INDICATOR_TYPES)) diff --git a/shared/src/business/entities/PrivatePractitioner.test.js b/shared/src/business/entities/PrivatePractitioner.test.js index 7d485d3a927..a1760e0021c 100644 --- a/shared/src/business/entities/PrivatePractitioner.test.js +++ b/shared/src/business/entities/PrivatePractitioner.test.js @@ -1,5 +1,5 @@ const { PrivatePractitioner } = require('./PrivatePractitioner'); -const { User } = require('./User'); +const { ROLES } = require('./EntityConstants'); describe('PrivatePractitioner', () => { it('Creates a valid PrivatePractitioner', () => { @@ -17,8 +17,8 @@ describe('PrivatePractitioner', () => { }, firstName: 'firstName', lastName: 'lastName', - role: User.ROLES.privatePractitioner, - userId: 'petitioner', + role: ROLES.privatePractitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); @@ -27,7 +27,7 @@ describe('PrivatePractitioner', () => { it('Creates an invalid', () => { const user = new PrivatePractitioner({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }); expect(user.isValid()).toBeFalsy(); }); diff --git a/shared/src/business/entities/PublicUser.js b/shared/src/business/entities/PublicUser.js index 9abc0eda6d3..1ff9a4d99f3 100644 --- a/shared/src/business/entities/PublicUser.js +++ b/shared/src/business/entities/PublicUser.js @@ -2,37 +2,20 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); -const { User } = require('./User'); +const { baseUserValidation: userValidation } = require('./User'); +const { ROLES } = require('./EntityConstants'); PublicUser.validationName = 'PublicUser'; const userDecorator = (obj, rawObj) => { obj.name = rawObj.name; obj.role = rawObj.role; - if (obj.role === User.ROLES.judge) { + if (obj.role === ROLES.judge) { obj.judgeFullName = rawObj.judgeFullName; obj.judgeTitle = rawObj.judgeTitle; } }; -const userValidation = { - judgeFullName: joi.when('role', { - is: User.ROLES.judge, - otherwise: joi.optional().allow(null), - then: joi.string().optional(), - }), - judgeTitle: joi.when('role', { - is: User.ROLES.judge, - otherwise: joi.optional().allow(null), - then: joi.string().optional(), - }), - name: joi.string().optional(), - role: joi - .string() - .valid(...Object.values(User.ROLES)) - .required(), -}; - const VALIDATION_ERROR_MESSAGES = { role: 'Role is required', }; diff --git a/shared/src/business/entities/Scan.js b/shared/src/business/entities/Scan.js index dc9a2b25edb..bc5a842f6b8 100644 --- a/shared/src/business/entities/Scan.js +++ b/shared/src/business/entities/Scan.js @@ -5,6 +5,7 @@ const { const { createISODateString } = require('../utilities/DateHandler'); const { getTimestampSchema } = require('../../utilities/dateSchema'); const { remove } = require('lodash'); + const joiStrictTimestamp = getTimestampSchema(); /** * constructor @@ -21,11 +22,6 @@ function Scan({ applicationContext, rawScan }) { } Scan.validationName = 'Scan'; -Scan.SCAN_MODES = { - DUPLEX: 'duplex', - FEEDER: 'feeder', - FLATBED: 'flatbed', -}; /** * adds a batch to the current scan diff --git a/shared/src/business/entities/Statistic.js b/shared/src/business/entities/Statistic.js index e93618db76d..e9cfe47bebd 100644 --- a/shared/src/business/entities/Statistic.js +++ b/shared/src/business/entities/Statistic.js @@ -12,67 +12,99 @@ const joiStrictTimestamp = getTimestampSchema(); * @param {object} rawStatistic the raw statistic data * @constructor */ -function Statistic(rawStatistic) { +function Statistic(rawStatistic, { applicationContext }) { + if (!applicationContext) { + throw new TypeError('applicationContext must be defined'); + } this.entityName = 'Statistic'; - this.deficiencyAmount = rawStatistic.deficiencyAmount; + this.determinationDeficiencyAmount = + rawStatistic.determinationDeficiencyAmount; + this.determinationTotalPenalties = rawStatistic.determinationTotalPenalties; + this.irsDeficiencyAmount = rawStatistic.irsDeficiencyAmount; + this.irsTotalPenalties = rawStatistic.irsTotalPenalties; this.lastDateOfPeriod = rawStatistic.lastDateOfPeriod; - this.totalPenalties = rawStatistic.totalPenalties; this.year = rawStatistic.year; this.yearOrPeriod = rawStatistic.yearOrPeriod; + this.statisticId = + rawStatistic.statisticId || applicationContext.getUniqueId(); } Statistic.validationName = 'Statistic'; Statistic.VALIDATION_ERROR_MESSAGES = { + determinationDeficiencyAmount: 'Enter deficiency as determined by Court', + determinationTotalPenalties: 'Enter total penalties as determined by Court', + irsDeficiencyAmount: 'Enter deficiency on IRS Notice', + irsTotalPenalties: 'Enter total penalties on IRS Notice', lastDateOfPeriod: [ { contains: 'must be less than or equal to', - message: 'Enter a valid last date of period', + message: 'Enter valid last date of period', }, - 'last date of period is required', + 'Enter last date of period', ], + year: 'Enter a valid year', }; joiValidationDecorator( Statistic, joi.object().keys({ - deficiencyAmount: joi + determinationDeficiencyAmount: joi + .alternatives() + .conditional('determinationTotalPenalties', { + is: joi.exist().not(null), + otherwise: joi.number().optional().allow(null), + then: joi.number().required(), + }) + .description('The amount of the deficiency determined by the Court.'), + determinationTotalPenalties: joi + .alternatives() + .conditional('determinationDeficiencyAmount', { + is: joi.exist().not(null), + otherwise: joi.number().optional().allow(null), + then: joi.number().required(), + }) + .description( + 'The total amount of penalties for the period or year determined by the Court.', + ), + entityName: joi.string().valid('Statistic').required(), + irsDeficiencyAmount: joi .number() .required() - .allow(null) - .description('The amount of the deficiency.'), - entityName: joi.string().valid('Statistic').required(), - lastDateOfPeriod: joi.when('yearOrPeriod', { - is: 'Period', - otherwise: joi - .optional() - .allow(null) - .description('Last date of the statistics period.'), - then: joiStrictTimestamp - .max('now') - .required() - .allow(null) - .description('Last date of the statistics period.'), - }), - totalPenalties: joi + .description('The amount of the deficiency on the IRS notice.'), + irsTotalPenalties: joi .number() .required() - .description('The total amount of penalties for the period or year.'), - year: joi.when('yearOrPeriod', { - is: 'Year', - otherwise: joi - .optional() - .allow(null) - .description('The year of the statistics period.'), - then: joi - .number() - .integer() - .required() - .min(1900) - .max(new Date().getFullYear()) - .description('The year of the statistics period.'), - }), + .description( + 'The total amount of penalties for the period or year on the IRS notice.', + ), + lastDateOfPeriod: joi + .when('yearOrPeriod', { + is: 'Period', + otherwise: joi.optional().allow(null), + then: joiStrictTimestamp.max('now').required(), + }) + .description('Last date of the statistics period.'), + statisticId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required() + .description('Unique statistic ID only used by the system.'), + year: joi + .when('yearOrPeriod', { + is: 'Year', + otherwise: joi.optional().allow(null), + then: joi + .number() + .integer() + .required() + .min(1900) + .max(new Date().getFullYear()), + }) + .description('The year of the statistics period.'), yearOrPeriod: joi .string() .required() diff --git a/shared/src/business/entities/Statistic.test.js b/shared/src/business/entities/Statistic.test.js index 1f1bad3a7b7..2bf1e84b2f5 100644 --- a/shared/src/business/entities/Statistic.test.js +++ b/shared/src/business/entities/Statistic.test.js @@ -1,11 +1,15 @@ +const { applicationContext } = require('../test/createTestApplicationContext'); const { Statistic } = require('./Statistic'); describe('Statistic', () => { describe('validation', () => { it('fails validation if a yearOrPeriod is an invalid value', () => { - const statistic = new Statistic({ - yearOrPeriod: 'something else', - }); + const statistic = new Statistic( + { + yearOrPeriod: 'something else', + }, + { applicationContext }, + ); expect(statistic.isValid()).toBeFalsy(); expect(Object.keys(statistic.getFormattedValidationErrors())).toContain( 'yearOrPeriod', @@ -13,37 +17,46 @@ describe('Statistic', () => { }); it('passes validation with minimal required information', () => { - const statistic = new Statistic({ - deficiencyAmount: 1, - totalPenalties: 1, - year: '2001', - yearOrPeriod: 'Year', - }); + const statistic = new Statistic( + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: '2001', + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); expect(statistic.isValid()).toBeTruthy(); }); - it('fails validation if a deficiencyAmount, totalPenalties, or year are not numbers', () => { - const statistic = new Statistic({ - deficiencyAmount: 'something else', - totalPenalties: 'something else', - year: 'something else', - yearOrPeriod: 'Year', - }); + it('fails validation if a irsDeficiencyAmount, irsTotalPenalties, or year are not numbers', () => { + const statistic = new Statistic( + { + irsDeficiencyAmount: 'something else', + irsTotalPenalties: 'something else', + year: 'something else', + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); expect(statistic.isValid()).toBeFalsy(); expect(Object.keys(statistic.getFormattedValidationErrors())).toEqual([ - 'deficiencyAmount', - 'totalPenalties', + 'irsDeficiencyAmount', + 'irsTotalPenalties', 'year', ]); }); it('fails validation if a lastDateOfPeriod is a date in the future', () => { - const statistic = new Statistic({ - deficiencyAmount: 1, - lastDateOfPeriod: '2050-03-01T21:40:46.415Z', - totalPenalties: 1, - yearOrPeriod: 'Period', - }); + const statistic = new Statistic( + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + lastDateOfPeriod: '2050-03-01T21:40:46.415Z', + yearOrPeriod: 'Period', + }, + { applicationContext }, + ); expect(statistic.isValid()).toBeFalsy(); expect(statistic.getFormattedValidationErrors()).toMatchObject({ lastDateOfPeriod: @@ -52,12 +65,15 @@ describe('Statistic', () => { }); it('fails validation if a year is in the future', () => { - const statistic = new Statistic({ - deficiencyAmount: 1, - totalPenalties: 1, - year: 2050, - yearOrPeriod: 'Year', - }); + const statistic = new Statistic( + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: 2050, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); expect(statistic.isValid()).toBeFalsy(); expect(Object.keys(statistic.getFormattedValidationErrors())).toEqual([ 'year', @@ -65,14 +81,53 @@ describe('Statistic', () => { }); it('passes validation with valid values', () => { - const statistic = new Statistic({ - deficiencyAmount: 654.32, - lastDateOfPeriod: '2015-03-01T21:40:46.415Z', - totalPenalties: 123.45, - year: 2015, - yearOrPeriod: 'Year', - }); + const statistic = new Statistic( + { + irsDeficiencyAmount: 654.32, + irsTotalPenalties: 123.45, + lastDateOfPeriod: '2015-03-01T21:40:46.415Z', + year: 2015, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); expect(statistic.isValid()).toBeTruthy(); }); + + it('requires determinationDeficiencyAmount be defined if determinationTotalPenalties is set', () => { + const statistic = new Statistic( + { + determinationTotalPenalties: 100.11, + irsDeficiencyAmount: 654.32, + irsTotalPenalties: 123.45, + lastDateOfPeriod: '2015-03-01T21:40:46.415Z', + year: 2015, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); + expect(statistic.isValid()).toBeFalsy(); + expect(Object.keys(statistic.getFormattedValidationErrors())).toEqual([ + 'determinationDeficiencyAmount', + ]); + }); + + it('requires determinationTotalPenalties be defined if determinationDeficiencyAmount is set', () => { + const statistic = new Statistic( + { + determinationDeficiencyAmount: 100.11, + irsDeficiencyAmount: 654.32, + irsTotalPenalties: 123.45, + lastDateOfPeriod: '2015-03-01T21:40:46.415Z', + year: 2015, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); + expect(statistic.isValid()).toBeFalsy(); + expect(Object.keys(statistic.getFormattedValidationErrors())).toEqual([ + 'determinationTotalPenalties', + ]); + }); }); }); diff --git a/shared/src/business/entities/User.js b/shared/src/business/entities/User.js index f43116a3f58..f1cfd75eb9b 100644 --- a/shared/src/business/entities/User.js +++ b/shared/src/business/entities/User.js @@ -5,32 +5,14 @@ const { const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); - -User.ROLES = { - adc: 'adc', - admin: 'admin', - admissionsClerk: 'admissionsclerk', - chambers: 'chambers', - clerkOfCourt: 'clerkofcourt', - docketClerk: 'docketclerk', - floater: 'floater', - inactivePractitioner: 'inactivePractitioner', - irsPractitioner: 'irsPractitioner', - irsSuperuser: 'irsSuperuser', - judge: 'judge', - petitioner: 'petitioner', - petitionsClerk: 'petitionsclerk', - privatePractitioner: 'privatePractitioner', - trialClerk: 'trialclerk', -}; +const { COUNTRY_TYPES, ROLES } = require('./EntityConstants'); const userDecorator = (obj, rawObj) => { obj.entityName = 'User'; obj.barNumber = rawObj.barNumber; obj.email = rawObj.email; obj.name = rawObj.name; - obj.role = rawObj.role || User.ROLES.petitioner; + obj.role = rawObj.role || ROLES.petitioner; obj.section = rawObj.section; obj.token = rawObj.token; obj.userId = rawObj.userId; @@ -47,69 +29,79 @@ const userDecorator = (obj, rawObj) => { state: rawObj.contact.state, }; } - if (obj.role === User.ROLES.judge) { + if (obj.role === ROLES.judge) { obj.judgeFullName = rawObj.judgeFullName; obj.judgeTitle = rawObj.judgeTitle; } }; +const baseUserValidation = { + judgeFullName: joi.when('role', { + is: ROLES.judge, + otherwise: joi.optional().allow(null), + then: joi.string().max(100).optional(), + }), + judgeTitle: joi.when('role', { + is: ROLES.judge, + otherwise: joi.optional().allow(null), + then: joi.string().max(100).optional(), + }), + name: joi.string().max(100).optional(), + role: joi + .string() + .valid(...Object.values(ROLES)) + .required(), +}; + const userValidation = { barNumber: joi.string().optional().allow(null), contact: joi .object() .keys({ - address1: joi.string().required(), - address2: joi.string().optional().allow(null), - address3: joi.string().optional().allow(null), - city: joi.string().required(), + address1: joi.string().max(100).required(), + address2: joi.string().max(100).optional().allow(null), + address3: joi.string().max(100).optional().allow(null), + city: joi.string().max(100).required(), country: joi.when('countryType', { - is: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, + is: COUNTRY_TYPES.INTERNATIONAL, otherwise: joi.string().optional().allow(null), then: joi.string().required(), }), countryType: joi .string() - .valid( - ContactFactory.COUNTRY_TYPES.DOMESTIC, - ContactFactory.COUNTRY_TYPES.INTERNATIONAL, - ) + .valid(COUNTRY_TYPES.DOMESTIC, COUNTRY_TYPES.INTERNATIONAL) .required(), - phone: joi.string().required(), + phone: joi.string().max(100).required(), postalCode: joi.when('countryType', { - is: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, + is: COUNTRY_TYPES.INTERNATIONAL, otherwise: JoiValidationConstants.US_POSTAL_CODE.required(), - then: joi.string().required(), + then: joi.string().max(100).required(), }), state: joi.when('countryType', { - is: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, - otherwise: joi.string().required(), + is: COUNTRY_TYPES.INTERNATIONAL, + otherwise: joi.string().max(100).required(), then: joi.string().optional().allow(null), }), }) .optional(), - email: joi.string().optional(), + email: joi.string().max(100).optional(), entityName: joi.string().valid('User').required(), - judgeFullName: joi.when('role', { - is: User.ROLES.judge, - otherwise: joi.optional().allow(null), - then: joi.string().optional(), - }), - judgeTitle: joi.when('role', { - is: User.ROLES.judge, - otherwise: joi.optional().allow(null), - then: joi.string().optional(), - }), - name: joi.string().optional(), - role: joi + section: joi .string() - .valid(...Object.values(User.ROLES)) - .required(), - section: joi.string().optional(), + // Removed temporarily: Eric will re-add + // .valid(...SECTIONS, ...CHAMBERS_SECTIONS, ...Object.values(ROLES)) + .optional(), token: joi.string().optional(), - userId: joi.string().required(), + userId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .required(), + ...baseUserValidation, }; const VALIDATION_ERROR_MESSAGES = { @@ -149,25 +141,25 @@ joiValidationDecorator( User.isExternalUser = function (role) { const externalRoles = [ - User.ROLES.petitioner, - User.ROLES.privatePractitioner, - User.ROLES.irsPractitioner, - User.ROLES.irsSuperuser, + ROLES.petitioner, + ROLES.privatePractitioner, + ROLES.irsPractitioner, + ROLES.irsSuperuser, ]; return externalRoles.includes(role); }; User.isInternalUser = function (role) { const internalRoles = [ - User.ROLES.adc, - User.ROLES.admissionsClerk, - User.ROLES.chambers, - User.ROLES.clerkOfCourt, - User.ROLES.docketClerk, - User.ROLES.floater, - User.ROLES.judge, - User.ROLES.petitionsClerk, - User.ROLES.trialClerk, + ROLES.adc, + ROLES.admissionsClerk, + ROLES.chambers, + ROLES.clerkOfCourt, + ROLES.docketClerk, + ROLES.floater, + ROLES.judge, + ROLES.petitionsClerk, + ROLES.trialClerk, ]; return internalRoles.includes(role); }; @@ -175,6 +167,7 @@ User.isInternalUser = function (role) { module.exports = { User, VALIDATION_ERROR_MESSAGES, + baseUserValidation, userDecorator, userValidation, }; diff --git a/shared/src/business/entities/User.test.js b/shared/src/business/entities/User.test.js index 5022e68499a..bae8537aaae 100644 --- a/shared/src/business/entities/User.test.js +++ b/shared/src/business/entities/User.test.js @@ -1,3 +1,4 @@ +const { ROLES } = require('./EntityConstants'); const { User } = require('./User'); describe('User entity', () => { @@ -16,8 +17,8 @@ describe('User entity', () => { }, firstName: 'firstName', lastName: 'lastName', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); }); @@ -36,8 +37,8 @@ describe('User entity', () => { }, firstName: 'firstName', lastName: 'lastName', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); }); @@ -54,8 +55,8 @@ describe('User entity', () => { }, firstName: 'firstName', lastName: 'lastName', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); }); @@ -64,8 +65,8 @@ describe('User entity', () => { const user = new User({ firstName: 'firstName', lastName: 'lastName', - role: User.ROLES.petitioner, - userId: 'Tester', + role: ROLES.petitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); expect(user.entityName).toEqual('User'); @@ -76,17 +77,17 @@ describe('User entity', () => { barNumber: 'gg', firstName: 'firstName', lastName: 'bob', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, token: 'abc', - userId: 'Tester', + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); }); it('Creates a valid irsPractitioner user', () => { const user = new User({ - role: User.ROLES.irsPractitioner, - userId: 'irsPractitioner', + role: ROLES.irsPractitioner, + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); expect(user.isValid()).toBeTruthy(); }); @@ -96,47 +97,47 @@ describe('User entity', () => { firstName: 'firstName', lastName: 'lastName', role: undefined, - userId: 'bobbymcgee', + userId: '3ab77c88-1dd0-4adb-a03c-c466ad72d417', }); - expect(user.role).toBe(User.ROLES.petitioner); + expect(user.role).toBe(ROLES.petitioner); }); describe('isExternalUser', () => { it('should return true when the user role is petitioner', () => { - expect(User.isExternalUser(User.ROLES.petitioner)).toEqual(true); + expect(User.isExternalUser(ROLES.petitioner)).toEqual(true); }); it('should return true when the user role is privatePractitioner', () => { - expect(User.isExternalUser(User.ROLES.privatePractitioner)).toEqual(true); + expect(User.isExternalUser(ROLES.privatePractitioner)).toEqual(true); }); it('should return true when the user role is irsPractitioner', () => { - expect(User.isExternalUser(User.ROLES.irsPractitioner)).toEqual(true); + expect(User.isExternalUser(ROLES.irsPractitioner)).toEqual(true); }); }); describe('isInternalUser', () => { it('should return true when the user role is docketclerk', () => { - expect(User.isInternalUser(User.ROLES.docketClerk)).toEqual(true); + expect(User.isInternalUser(ROLES.docketClerk)).toEqual(true); }); it('should return true when the user role is petitionsclerk', () => { - expect(User.isInternalUser(User.ROLES.petitionsClerk)).toEqual(true); + expect(User.isInternalUser(ROLES.petitionsClerk)).toEqual(true); }); it('should return true when the user role is judge', () => { - expect(User.isInternalUser(User.ROLES.judge)).toEqual(true); + expect(User.isInternalUser(ROLES.judge)).toEqual(true); }); it('should return true when the user role is adc', () => { - expect(User.isInternalUser(User.ROLES.adc)).toEqual(true); + expect(User.isInternalUser(ROLES.adc)).toEqual(true); }); it('should return true when the user role is admissionsclerk', () => { - expect(User.isInternalUser(User.ROLES.admissionsClerk)).toEqual(true); + expect(User.isInternalUser(ROLES.admissionsClerk)).toEqual(true); }); it('should return true when the user role is chambers', () => { - expect(User.isInternalUser(User.ROLES.chambers)).toEqual(true); + expect(User.isInternalUser(ROLES.chambers)).toEqual(true); }); it('should return true when the user role is clerkofcourt', () => { - expect(User.isInternalUser(User.ROLES.clerkOfCourt)).toEqual(true); + expect(User.isInternalUser(ROLES.clerkOfCourt)).toEqual(true); }); it('should return true when the user role is trialclerk', () => { - expect(User.isInternalUser(User.ROLES.trialClerk)).toEqual(true); + expect(User.isInternalUser(ROLES.trialClerk)).toEqual(true); }); }); }); diff --git a/shared/src/business/entities/UserCase.js b/shared/src/business/entities/UserCase.js new file mode 100644 index 00000000000..022d10430df --- /dev/null +++ b/shared/src/business/entities/UserCase.js @@ -0,0 +1,40 @@ +const joi = require('@hapi/joi'); +const { + joiValidationDecorator, +} = require('../../utilities/JoiValidationDecorator'); +const { Case } = require('./cases/Case'); + +UserCase.validationName = 'UserCase'; + +/** + * UserCase Entity + * represents a user-to-case mapping record + * + * @param {object} rawUserCase the raw user-case data + * @constructor + */ +function UserCase(rawUserCase) { + this.entityName = 'UserCase'; + this.caseId = rawUserCase.caseId; + this.caseCaption = rawUserCase.caseCaption; + this.createdAt = rawUserCase.createdAt; + this.docketNumber = rawUserCase.docketNumber; + this.docketNumberWithSuffix = rawUserCase.docketNumberWithSuffix; + this.leadCaseId = rawUserCase.leadCaseId; + this.status = rawUserCase.status; +} + +joiValidationDecorator( + UserCase, + joi.object().keys({ + caseCaption: Case.VALIDATION_RULES.caseCaption, + caseId: Case.VALIDATION_RULES.caseId, + docketNumber: Case.VALIDATION_RULES.docketNumber, + docketNumberWithSuffix: Case.VALIDATION_RULES.docketNumberWithSuffix, + leadCaseId: Case.VALIDATION_RULES.leadCaseId, + status: Case.VALIDATION_RULES.status, + }), + Case.VALIDATION_ERROR_MESSAGES, +); + +exports.UserCase = UserCase; diff --git a/shared/src/business/entities/UserCase.test.js b/shared/src/business/entities/UserCase.test.js new file mode 100644 index 00000000000..488c77bdb27 --- /dev/null +++ b/shared/src/business/entities/UserCase.test.js @@ -0,0 +1,21 @@ +const { prepareDateFromString } = require('../utilities/DateHandler'); +const { UserCase } = require('./UserCase'); + +describe('UserCase', () => { + it('fails validation if required fields are non-existent', () => { + expect(new UserCase({}).isValid()).toBeFalsy(); + }); + + it('passes validation if required fields exist', () => { + expect( + new UserCase({ + caseCaption: 'Guy Fieri, Petitioner', + caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + createdAt: prepareDateFromString().toISOString(), + docketNumber: '104-21', + docketNumberWithSuffix: '104-20W', + leadCaseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + }).isValid(), + ).toBeTruthy(); + }); +}); diff --git a/shared/src/business/entities/WorkItem.js b/shared/src/business/entities/WorkItem.js index ec7452527c8..779d19307b0 100644 --- a/shared/src/business/entities/WorkItem.js +++ b/shared/src/business/entities/WorkItem.js @@ -2,12 +2,23 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../utilities/JoiValidationDecorator'); -const { CHIEF_JUDGE } = require('./cases/CaseConstants'); +const { CHIEF_JUDGE, ROLES } = require('./EntityConstants'); const { createISODateString } = require('../utilities/DateHandler'); const { getTimestampSchema } = require('../../utilities/dateSchema'); const { Message } = require('./Message'); const { omit, orderBy } = require('lodash'); const joiStrictTimestamp = getTimestampSchema(); +const { + CASE_STATUS_TYPES, + DOCKET_NUMBER_MATCHER, + DOCKET_NUMBER_SUFFIXES, +} = require('./EntityConstants'); +const { + CHAMBERS_SECTIONS, + IRS_SYSTEM_SECTION, + SECTIONS, +} = require('./EntityConstants'); + /** * constructor * @@ -18,11 +29,9 @@ function WorkItem(rawWorkItem, { applicationContext }) { if (!applicationContext) { throw new TypeError('applicationContext must be defined'); } - this.entityName = 'WorkItem'; - - this.associatedJudge = rawWorkItem.associatedJudge || CHIEF_JUDGE; this.assigneeId = rawWorkItem.assigneeId; this.assigneeName = rawWorkItem.assigneeName; + this.associatedJudge = rawWorkItem.associatedJudge || CHIEF_JUDGE; this.caseId = rawWorkItem.caseId; this.caseIsInProgress = rawWorkItem.caseIsInProgress; this.caseStatus = rawWorkItem.caseStatus; @@ -35,6 +44,7 @@ function WorkItem(rawWorkItem, { applicationContext }) { this.docketNumber = rawWorkItem.docketNumber; this.docketNumberWithSuffix = rawWorkItem.docketNumberWithSuffix; this.document = omit(rawWorkItem.document, 'workItems'); + this.entityName = 'WorkItem'; this.hideFromPendingMessages = rawWorkItem.hideFromPendingMessages; this.highPriority = rawWorkItem.highPriority; this.inProgress = rawWorkItem.inProgress; @@ -48,6 +58,7 @@ function WorkItem(rawWorkItem, { applicationContext }) { this.trialDate = rawWorkItem.trialDate; this.updatedAt = rawWorkItem.updatedAt || createISODateString(); this.workItemId = rawWorkItem.workItemId || applicationContext.getUniqueId(); + this.messages = (rawWorkItem.messages || []).map( message => new Message(message, { applicationContext }), ); @@ -58,9 +69,15 @@ WorkItem.validationName = 'WorkItem'; joiValidationDecorator( WorkItem, joi.object().keys({ - assigneeId: joi.string().allow(null).optional(), - assigneeName: joi.string().allow(null).optional(), // should be a Message entity at some point - associatedJudge: joi.string().required(), + assigneeId: joi + .string() + .uuid({ + version: ['uuidv4'], + }) + .allow(null) + .optional(), + assigneeName: joi.string().max(100).allow(null).optional(), // should be a Message entity at some point + associatedJudge: joi.string().max(100).required(), caseId: joi .string() .uuid({ @@ -68,10 +85,13 @@ joiValidationDecorator( }) .required(), caseIsInProgress: joi.boolean().optional(), - caseStatus: joi.string().optional(), - caseTitle: joi.string().optional(), + caseStatus: joi + .string() + .valid(...Object.values(CASE_STATUS_TYPES)) + .optional(), + caseTitle: joi.string().max(500).optional(), completedAt: joiStrictTimestamp.optional(), - completedBy: joi.string().optional().allow(null), + completedBy: joi.string().max(100).optional().allow(null), completedByUserId: joi .string() .uuid({ @@ -79,10 +99,14 @@ joiValidationDecorator( }) .optional() .allow(null), - completedMessage: joi.string().optional().allow(null), + completedMessage: joi.string().max(100).optional().allow(null), createdAt: joiStrictTimestamp.optional(), - docketNumber: joi.string().required(), - docketNumberSuffix: joi.string().allow(null).optional(), + docketNumber: joi.string().regex(DOCKET_NUMBER_MATCHER).required(), + docketNumberSuffix: joi + .string() + .valid(...Object.values(DOCKET_NUMBER_SUFFIXES)) + .allow(null) + .optional(), document: joi.object().required(), entityName: joi.string().valid('WorkItem').required(), hideFromPendingMessages: joi.boolean().optional(), @@ -92,9 +116,20 @@ joiValidationDecorator( isQC: joi.boolean().required(), isRead: joi.boolean().optional(), messages: joi.array().items(joi.object()).required(), - section: joi.string().required(), - sentBy: joi.string().required(), - sentBySection: joi.string().optional(), + section: joi + .string() + .valid( + ...SECTIONS, + ...CHAMBERS_SECTIONS, + ...Object.values(ROLES), + IRS_SYSTEM_SECTION, + ) + .required(), + sentBy: joi.string().max(100).required(), + sentBySection: joi + .string() + .valid(...SECTIONS, ...CHAMBERS_SECTIONS, ...Object.values(ROLES)) + .optional(), sentByUserId: joi .string() .uuid({ diff --git a/shared/src/business/entities/WorkItem.test.js b/shared/src/business/entities/WorkItem.test.js index fa54ce9f89a..bc637ba1752 100644 --- a/shared/src/business/entities/WorkItem.test.js +++ b/shared/src/business/entities/WorkItem.test.js @@ -1,5 +1,5 @@ const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('./cases/Case'); +const { CASE_STATUS_TYPES } = require('./EntityConstants'); const { Message } = require('./Message'); const { WorkItem } = require('./WorkItem'); @@ -12,10 +12,10 @@ describe('WorkItem', () => { it('Creates a valid workitem', () => { const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -33,10 +33,10 @@ describe('WorkItem', () => { it('Update a valid workitem with a workItemId', () => { const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -55,10 +55,10 @@ describe('WorkItem', () => { it('Update a valid workitem with a isRead', () => { const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -78,10 +78,10 @@ describe('WorkItem', () => { it('Create a valid workitem without messages', () => { const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -98,10 +98,10 @@ describe('WorkItem', () => { it('Create a valid workitem with real message', () => { const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -127,10 +127,10 @@ describe('WorkItem', () => { it('when calling add message', () => { const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -157,10 +157,10 @@ describe('WorkItem', () => { it('no message added when set as completed', () => { const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', diff --git a/shared/src/business/entities/WorkQueue.js b/shared/src/business/entities/WorkQueue.js deleted file mode 100644 index 6864d0f03e9..00000000000 --- a/shared/src/business/entities/WorkQueue.js +++ /dev/null @@ -1,84 +0,0 @@ -const { sortBy } = require('lodash'); - -exports.ADC_SECTION = 'adc'; -exports.ADMISSIONS_SECTION = 'admissions'; -exports.CHAMBERS_SECTION = 'chambers'; -exports.CLERK_OF_COURT_SECTION = 'clerkofcourt'; -exports.DOCKET_SECTION = 'docket'; -exports.PETITIONS_SECTION = 'petitions'; -exports.IRS_SYSTEM_SECTION = 'irsSystem'; -exports.TRIAL_CLERKS_SECTION = 'trialClerks'; - -exports.ARMENS_CHAMBERS_SECTION = 'armensChambers'; -exports.ASHFORDS_CHAMBERS_SECTION = 'ashfordsChambers'; -exports.BUCHS_CHAMBERS_SECTION = 'buchsChambers'; -exports.CARLUZZOS_CHAMBERS_SECTION = 'carluzzosChambers'; -exports.COHENS_CHAMBERS_SECTION = 'cohensChambers'; -exports.COLVINS_CHAMBERS_SECTION = 'colvinsChambers'; -exports.COPELANDS_CHAMBERS_SECTION = 'copelandsChambers'; -exports.FOLEYS_CHAMBERS_SECTION = 'foleysChambers'; -exports.GALES_CHAMBERS_SECTION = 'galesChambers'; -exports.GERBERS_CHAMBERS_SECTION = 'gerbersChambers'; -exports.GOEKES_CHAMBERS_SECTION = 'goekesChambers'; -exports.GUSTAFSONS_CHAMBERS_SECTION = 'gustafsonsChambers'; -exports.GUYS_CHAMBERS_SECTION = 'guysChambers'; -exports.HALPERNS_CHAMBERS_SECTION = 'halpernsChambers'; -exports.HOLMES_CHAMBERS_SECTION = 'holmesChambers'; -exports.JACOBS_CHAMBERS_SECTION = 'jacobsChambers'; -exports.KERRIGANS_CHAMBERS_SECTION = 'kerrigansChambers'; -exports.LAUBERS_CHAMBERS_SECTION = 'laubersChambers'; -exports.LEYDENS_CHAMBERS_SECTION = 'leydensChambers'; -exports.MARVELS_CHAMBERS_SECTION = 'marvelsChambers'; -exports.MORRISONS_CHAMBERS_SECTION = 'morrisonsChambers'; -exports.NEGAS_CHAMBERS_SECTION = 'negasChambers'; -exports.PANUTHOS_CHAMBERS_SECTION = 'panuthosChambers'; -exports.PARIS_CHAMBERS_SECTION = 'parisChambers'; -exports.PUGHS_CHAMBERS_SECTION = 'pughsChambers'; -exports.RUWES_CHAMBERS_SECTION = 'ruwesChambers'; -exports.THORNTONS_CHAMBERS_SECTION = 'thorntonsChambers'; -exports.URDAS_CHAMBERS_SECTION = 'urdasChambers'; -exports.VASQUEZS_CHAMBERS_SECTION = 'vasquezsChambers'; -exports.WELLS_CHAMBERS_SECTION = 'wellsChambers'; - -exports.SECTIONS = sortBy([ - exports.ADC_SECTION, - exports.ADMISSIONS_SECTION, - exports.CHAMBERS_SECTION, - exports.CLERK_OF_COURT_SECTION, - exports.DOCKET_SECTION, - exports.PETITIONS_SECTION, - exports.TRIAL_CLERKS_SECTION, -]); - -exports.CHAMBERS_SECTIONS = sortBy([ - exports.ARMENS_CHAMBERS_SECTION, - exports.ASHFORDS_CHAMBERS_SECTION, - exports.BUCHS_CHAMBERS_SECTION, - exports.CARLUZZOS_CHAMBERS_SECTION, - exports.COHENS_CHAMBERS_SECTION, - exports.COLVINS_CHAMBERS_SECTION, - exports.COPELANDS_CHAMBERS_SECTION, - exports.FOLEYS_CHAMBERS_SECTION, - exports.GALES_CHAMBERS_SECTION, - exports.GERBERS_CHAMBERS_SECTION, - exports.GOEKES_CHAMBERS_SECTION, - exports.GUSTAFSONS_CHAMBERS_SECTION, - exports.GUYS_CHAMBERS_SECTION, - exports.HALPERNS_CHAMBERS_SECTION, - exports.HOLMES_CHAMBERS_SECTION, - exports.JACOBS_CHAMBERS_SECTION, - exports.KERRIGANS_CHAMBERS_SECTION, - exports.LAUBERS_CHAMBERS_SECTION, - exports.LEYDENS_CHAMBERS_SECTION, - exports.MARVELS_CHAMBERS_SECTION, - exports.MORRISONS_CHAMBERS_SECTION, - exports.NEGAS_CHAMBERS_SECTION, - exports.PANUTHOS_CHAMBERS_SECTION, - exports.PARIS_CHAMBERS_SECTION, - exports.PUGHS_CHAMBERS_SECTION, - exports.RUWES_CHAMBERS_SECTION, - exports.THORNTONS_CHAMBERS_SECTION, - exports.URDAS_CHAMBERS_SECTION, - exports.VASQUEZS_CHAMBERS_SECTION, - exports.WELLS_CHAMBERS_SECTION, -]); diff --git a/shared/src/business/entities/caseAssociation/AddIrsPractitioner.test.js b/shared/src/business/entities/caseAssociation/AddIrsPractitioner.test.js index e79eb672e52..acc952151ef 100644 --- a/shared/src/business/entities/caseAssociation/AddIrsPractitioner.test.js +++ b/shared/src/business/entities/caseAssociation/AddIrsPractitioner.test.js @@ -10,7 +10,9 @@ describe('AddIrsPractitioner', () => { }); it('should be valid when all fields are present', () => { - const entity = new AddIrsPractitioner({ user: { userId: 'abc' } }); + const entity = new AddIrsPractitioner({ + user: { userId: '02323349-87fe-4d29-91fe-8dd6916d2fda' }, + }); expect(entity.getFormattedValidationErrors()).toEqual(null); }); }); diff --git a/shared/src/business/entities/caseAssociation/AddPrivatePractitionerFactory.test.js b/shared/src/business/entities/caseAssociation/AddPrivatePractitionerFactory.test.js index 904c363bfe5..4c2158570ee 100644 --- a/shared/src/business/entities/caseAssociation/AddPrivatePractitionerFactory.test.js +++ b/shared/src/business/entities/caseAssociation/AddPrivatePractitionerFactory.test.js @@ -17,7 +17,7 @@ describe('AddPrivatePractitionerFactory', () => { it('should be valid when all fields are present', () => { const entity = AddPrivatePractitionerFactory.get({ representingPrimary: true, - user: { userId: 'abc' }, + user: { userId: '02323349-87fe-4d29-91fe-8dd6916d2fda' }, }); expect(entity.getFormattedValidationErrors()).toEqual(null); }); @@ -25,7 +25,7 @@ describe('AddPrivatePractitionerFactory', () => { it('should not be valid if representingPrimary is false and representingSecondary is not present', () => { const entity = AddPrivatePractitionerFactory.get({ representingPrimary: false, - user: { userId: 'abc' }, + user: { userId: '02323349-87fe-4d29-91fe-8dd6916d2fda' }, }); expect(entity.getFormattedValidationErrors()).toEqual({ representingPrimary: errorMessages.representingPrimary, diff --git a/shared/src/business/entities/cases/Case.js b/shared/src/business/entities/cases/Case.js index f91f54ecccd..d9a4edb921f 100644 --- a/shared/src/business/entities/cases/Case.js +++ b/shared/src/business/entities/cases/Case.js @@ -1,21 +1,41 @@ const joi = require('@hapi/joi'); +const { + ANSWER_CUTOFF_AMOUNT_IN_DAYS, + ANSWER_DOCUMENT_CODES, + AUTOMATIC_BLOCKED_REASONS, + CASE_CAPTION_POSTFIX, + CASE_STATUS_TYPES, + CASE_TYPES, + CASE_TYPES_MAP, + CHIEF_JUDGE, + COURT_ISSUED_EVENT_CODES, + DOCKET_NUMBER_MATCHER, + DOCKET_NUMBER_SUFFIXES, + FILING_TYPES, + INITIAL_DOCUMENT_TYPES, + MAX_FILE_SIZE_MB, + ORDER_TYPES, + PARTY_TYPES, + PAYMENT_STATUS, + PROCEDURE_TYPES, + ROLES, + TRIAL_CITY_STRINGS, + TRIAL_LOCATION_MATCHER, +} = require('../EntityConstants'); const { calculateDifferenceInDays, createISODateString, formatDateString, + PATTERNS, prepareDateFromString, } = require('../../utilities/DateHandler'); -const { - CHIEF_JUDGE, - DOCKET_NUMBER_MATCHER, - TRIAL_LOCATION_MATCHER, -} = require('./CaseConstants'); const { getDocketNumberSuffix, } = require('../../utilities/getDocketNumberSuffix'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); +const { compareStrings } = require('../../utilities/sortFunctions'); const { ContactFactory } = require('../contacts/ContactFactory'); const { Correspondence } = require('../Correspondence'); const { DocketRecord } = require('../DocketRecord'); @@ -23,127 +43,10 @@ const { Document } = require('../Document'); const { find, includes, isEmpty } = require('lodash'); const { getTimestampSchema } = require('../../../utilities/dateSchema'); const { IrsPractitioner } = require('../IrsPractitioner'); -const { MAX_FILE_SIZE_MB } = require('../../../persistence/s3/getUploadPolicy'); -const { Order } = require('../orders/Order'); const { PrivatePractitioner } = require('../PrivatePractitioner'); const { Statistic } = require('../Statistic'); -const { TrialSession } = require('../trialSessions/TrialSession'); const { User } = require('../User'); const joiStrictTimestamp = getTimestampSchema(); -const orderDocumentTypes = Order.ORDER_TYPES.map( - orderType => orderType.documentType, -); -const courtIssuedDocumentTypes = Document.COURT_ISSUED_EVENT_CODES.map( - courtIssuedDoc => courtIssuedDoc.documentType, -); - -Case.PAYMENT_STATUS = { - PAID: 'Paid', - UNPAID: 'Not Paid', - WAIVED: 'Waived', -}; - -Case.STATUS_TYPES = { - assignedCase: 'Assigned - Case', // Case has been assigned to a judge - assignedMotion: 'Assigned - Motion', // Someone has requested a judge for the case - calendared: 'Calendared', // Case has been scheduled for trial - cav: 'CAV', // Core alternative valuation - closed: 'Closed', // Judge has made a ruling to close the case - generalDocket: 'General Docket - Not at Issue', // Submitted to the IRS - generalDocketReadyForTrial: 'General Docket - At Issue (Ready for Trial)', // Case is ready for trial - jurisdictionRetained: 'Jurisdiction Retained', // Jurisdiction of a case is retained by a specific judge — usually after the case is on a judge’s trial calendar - new: 'New', // Case has not been QCed - onAppeal: 'On Appeal', // After the trial, the case has gone to the appeals court - rule155: 'Rule 155', // Where the Court has filed or stated its opinion or issued a dispositive order determining the issues in a case, it may withhold entry of its decision for the purpose of permitting the parties to submit computations pursuant to the Court’s determination of the issues, showing the correct amount to be included in the decision. - submitted: 'Submitted', // Submitted to the judge for decision -}; - -Case.STATUS_TYPES_WITH_ASSOCIATED_JUDGE = [ - Case.STATUS_TYPES.assignedCase, - Case.STATUS_TYPES.assignedMotion, - Case.STATUS_TYPES.cav, - Case.STATUS_TYPES.jurisdictionRetained, - Case.STATUS_TYPES.rule155, - Case.STATUS_TYPES.submitted, -]; - -Case.STATUS_TYPES_MANUAL_UPDATE = [ - Case.STATUS_TYPES.assignedCase, - Case.STATUS_TYPES.assignedMotion, - Case.STATUS_TYPES.cav, - Case.STATUS_TYPES.closed, - Case.STATUS_TYPES.generalDocket, - Case.STATUS_TYPES.generalDocketReadyForTrial, - Case.STATUS_TYPES.jurisdictionRetained, - Case.STATUS_TYPES.onAppeal, - Case.STATUS_TYPES.rule155, - Case.STATUS_TYPES.submitted, -]; - -Case.ANSWER_CUTOFF_AMOUNT_IN_DAYS = 45; -Case.ANSWER_CUTOFF_UNIT = 'day'; - -Case.CASE_TYPES_MAP = { - cdp: 'CDP (Lien/Levy)', - deficiency: 'Deficiency', - djExemptOrg: 'Declaratory Judgment (Exempt Organization)', - djRetirementPlan: 'Declaratory Judgment (Retirement Plan)', - innocentSpouse: 'Innocent Spouse', - interestAbatement: 'Interest Abatement', - other: 'Other', - partnershipSection1101: 'Partnership (BBA Section 1101)', - partnershipSection6226: 'Partnership (Section 6226)', - partnershipSection6228: 'Partnership (Section 6228)', - passport: 'Passport', - whistleblower: 'Whistleblower', - workerClassification: 'Worker Classification', -}; - -Case.CASE_TYPES = Object.values(Case.CASE_TYPES_MAP); - -// This is the order that they appear in the UI -Case.PROCEDURE_TYPES = ['Regular', 'Small']; - -Case.FILING_TYPES = { - [User.ROLES.petitioner]: [ - 'Myself', - 'Myself and my spouse', - 'A business', - 'Other', - ], - [User.ROLES.privatePractitioner]: [ - 'Individual petitioner', - 'Petitioner and spouse', - 'A business', - 'Other', - ], -}; - -Case.CASE_CAPTION_POSTFIX = 'v. Commissioner of Internal Revenue, Respondent'; - -Case.ANSWER_DOCUMENT_CODES = [ - 'A', - 'AAAP', - 'AAPN', - 'AATP', - 'AATS', - 'AATT', - 'APA', - 'ASAP', - 'ASUP', - 'ATAP', - 'ATSP', -]; - -Case.AUTOMATIC_BLOCKED_REASONS = { - dueDate: 'Due Date', - pending: 'Pending Item', - pendingAndDueDate: 'Pending Item and Due Date', -}; - -Case.CHIEF_JUDGE = CHIEF_JUDGE; - -Case.DOCKET_NUMBER_SUFFIXES = ['W', 'P', 'X', 'R', 'SL', 'L', 'S']; Case.VALIDATION_ERROR_MESSAGES = { applicationForWaiverOfFilingFeeFile: @@ -239,7 +142,7 @@ function Case(rawCase, { applicationContext, filtered = false }) { !filtered || User.isInternalUser(applicationContext.getCurrentUser().role) ) { - this.associatedJudge = rawCase.associatedJudge || Case.CHIEF_JUDGE; + this.associatedJudge = rawCase.associatedJudge || CHIEF_JUDGE; this.automaticBlocked = rawCase.automaticBlocked; this.automaticBlockedDate = rawCase.automaticBlockedDate; this.automaticBlockedReason = rawCase.automaticBlockedReason; @@ -247,10 +150,12 @@ function Case(rawCase, { applicationContext, filtered = false }) { this.blockedDate = rawCase.blockedDate; this.blockedReason = rawCase.blockedReason; this.caseNote = rawCase.caseNote; + this.damages = rawCase.damages; this.highPriority = rawCase.highPriority; this.highPriorityReason = rawCase.highPriorityReason; + this.litigationCosts = rawCase.litigationCosts; this.qcCompleteForTrial = rawCase.qcCompleteForTrial || {}; - this.status = rawCase.status || Case.STATUS_TYPES.new; + this.status = rawCase.status || CASE_STATUS_TYPES.new; this.userId = rawCase.userId; if (Array.isArray(rawCase.statistics)) { @@ -267,7 +172,9 @@ function Case(rawCase, { applicationContext, filtered = false }) { this.caseType = rawCase.caseType; this.closedDate = rawCase.closedDate; this.createdAt = rawCase.createdAt || createISODateString(); - this.docketNumber = rawCase.docketNumber; + if (rawCase.docketNumber) { + this.docketNumber = rawCase.docketNumber.replace(/^0+/, ''); // strip leading zeroes + } this.docketNumberSuffix = getDocketNumberSuffix(rawCase); this.filingType = rawCase.filingType; this.hasVerifiedIrsNotice = rawCase.hasVerifiedIrsNotice; @@ -280,7 +187,7 @@ function Case(rawCase, { applicationContext, filtered = false }) { this.petitionPaymentDate = rawCase.petitionPaymentDate; this.petitionPaymentMethod = rawCase.petitionPaymentMethod; this.petitionPaymentStatus = - rawCase.petitionPaymentStatus || Case.PAYMENT_STATUS.UNPAID; + rawCase.petitionPaymentStatus || PAYMENT_STATUS.UNPAID; this.petitionPaymentWaivedDate = rawCase.petitionPaymentWaivedDate; this.preferredTrialCity = rawCase.preferredTrialCity; this.procedureType = rawCase.procedureType; @@ -311,7 +218,7 @@ function Case(rawCase, { applicationContext, filtered = false }) { correspondence => new Correspondence(correspondence, { applicationContext }), ) - .sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1)); + .sort((a, b) => compareStrings(a.createdAt, b.createdAt)); } else { this.correspondence = []; } @@ -319,7 +226,7 @@ function Case(rawCase, { applicationContext, filtered = false }) { if (Array.isArray(rawCase.documents)) { this.documents = rawCase.documents .map(document => new Document(document, { applicationContext })) - .sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1)); + .sort((a, b) => compareStrings(a.createdAt, b.createdAt)); } else { this.documents = []; } @@ -385,7 +292,7 @@ function Case(rawCase, { applicationContext, filtered = false }) { this.contactSecondary = contacts.secondary; } -Case.validationRules = { +Case.VALIDATION_RULES = { associatedJudge: joi .string() .max(50) @@ -408,7 +315,7 @@ Case.validationRules = { otherwise: joi.optional().allow(null), then: joi .string() - .valid(...Object.values(Case.AUTOMATIC_BLOCKED_REASONS)) + .valid(...Object.values(AUTOMATIC_BLOCKED_REASONS)) .required() .description('The reason the case was automatically blocked from trial.'), }), @@ -458,10 +365,10 @@ Case.validationRules = { .meta({ tags: ['Restricted'] }), caseType: joi .string() - .valid(...Case.CASE_TYPES) + .valid(...CASE_TYPES) .required(), closedDate: joi.when('status', { - is: Case.STATUS_TYPES.closed, + is: CASE_STATUS_TYPES.closed, otherwise: joi.optional().allow(null), then: joiStrictTimestamp.required(), }), @@ -476,6 +383,11 @@ Case.validationRules = { .description( 'When the paper or electronic case was added to the system. This value cannot be edited.', ), + damages: joi + .number() + .optional() + .allow(null) + .description('Damages for the case.'), docketNumber: joi .string() .regex(DOCKET_NUMBER_MATCHER) @@ -484,7 +396,7 @@ Case.validationRules = { docketNumberSuffix: joi .string() .allow(null) - .valid(...Object.values(Case.DOCKET_NUMBER_SUFFIXES)) + .valid(...Object.values(DOCKET_NUMBER_SUFFIXES)) .optional(), docketNumberWithSuffix: joi .string() @@ -505,8 +417,8 @@ Case.validationRules = { filingType: joi .string() .valid( - ...Case.FILING_TYPES[User.ROLES.petitioner], - ...Case.FILING_TYPES[User.ROLES.privatePractitioner], + ...FILING_TYPES[ROLES.petitioner], + ...FILING_TYPES[ROLES.privatePractitioner], ) .optional(), hasVerifiedIrsNotice: joi @@ -560,6 +472,11 @@ Case.validationRules = { .description( 'If this case is consolidated, this is the ID of the lead case. It is the lowest docket number in the consolidated group.', ), + litigationCosts: joi + .number() + .optional() + .allow(null) + .description('Litigation costs for the case.'), mailingDate: joi .when('isPaper', { is: true, @@ -616,31 +533,31 @@ Case.validationRules = { .description('Reminder for clerks to review the Order to Show Cause.'), partyType: joi .string() - .valid(...Object.values(ContactFactory.PARTY_TYPES)) + .valid(...Object.values(PARTY_TYPES)) .required() .description('Party type of the case petitioner.'), petitionPaymentDate: joi .when('petitionPaymentStatus', { - is: Case.PAYMENT_STATUS.PAID, + is: PAYMENT_STATUS.PAID, otherwise: joiStrictTimestamp.optional().allow(null), then: joiStrictTimestamp.required(), }) .description('When the petitioner paid the case fee.'), petitionPaymentMethod: joi .when('petitionPaymentStatus', { - is: Case.PAYMENT_STATUS.PAID, + is: PAYMENT_STATUS.PAID, otherwise: joi.string().allow(null).optional(), then: joi.string().max(50).required(), }) .description('How the petitioner paid the case fee.'), petitionPaymentStatus: joi .string() - .valid(...Object.values(Case.PAYMENT_STATUS)) + .valid(...Object.values(PAYMENT_STATUS)) .required() .description('Status of the case fee payment.'), petitionPaymentWaivedDate: joi .when('petitionPaymentStatus', { - is: Case.PAYMENT_STATUS.WAIVED, + is: PAYMENT_STATUS.WAIVED, otherwise: joiStrictTimestamp.allow(null).optional(), then: joiStrictTimestamp.required(), }) @@ -648,7 +565,7 @@ Case.validationRules = { preferredTrialCity: joi .alternatives() .try( - joi.string().valid(...TrialSession.TRIAL_CITY_STRINGS, null), + joi.string().valid(...TRIAL_CITY_STRINGS, null), joi.string().pattern(TRIAL_LOCATION_MATCHER), // Allow unique values for testing ) .optional() @@ -659,7 +576,7 @@ Case.validationRules = { .description('List of private practitioners associated with the case.'), procedureType: joi .string() - .valid(...Case.PROCEDURE_TYPES) + .valid(...PROCEDURE_TYPES) .required() .description('Procedure type of the case.'), qcCompleteForTrial: joi @@ -692,7 +609,7 @@ Case.validationRules = { .items(joi.object().meta({ entityName: 'Statistic' })) .optional(), then: joi.when('caseType', { - is: Case.CASE_TYPES_MAP.deficiency, + is: CASE_TYPES_MAP.deficiency, otherwise: joi .array() .items(joi.object().meta({ entityName: 'Statistic' })) @@ -707,7 +624,7 @@ Case.validationRules = { .description('List of Statistic Entities for the case.'), status: joi .string() - .valid(...Object.values(Case.STATUS_TYPES)) + .valid(...Object.values(CASE_STATUS_TYPES)) .optional() .meta({ tags: ['Restricted'] }) .description('Status of the case.'), @@ -718,7 +635,7 @@ Case.validationRules = { trialLocation: joi .alternatives() .try( - joi.string().valid(...TrialSession.TRIAL_CITY_STRINGS, null), + joi.string().valid(...TRIAL_CITY_STRINGS, null), joi.string().pattern(TRIAL_LOCATION_MATCHER), // Allow unique values for testing ) .optional() @@ -736,7 +653,7 @@ Case.validationRules = { ), trialTime: joi .string() - .pattern(/^[0-9]{1,2}:([0-5][0-9])$/) + .pattern(PATTERNS['H:MM']) .optional() .description('Time of day when this case goes to trial.'), useSameAsPrimary: joi @@ -760,10 +677,15 @@ Case.validationRules = { joiValidationDecorator( Case, - joi.object().keys(Case.validationRules), + joi.object().keys(Case.VALIDATION_RULES), Case.VALIDATION_ERROR_MESSAGES, ); +const orderDocumentTypes = ORDER_TYPES.map(orderType => orderType.documentType); +const courtIssuedDocumentTypes = COURT_ISSUED_EVENT_CODES.map( + courtIssuedDoc => courtIssuedDoc.documentType, +); + /** * builds the case caption from case contact name(s) based on party type * @@ -773,56 +695,56 @@ joiValidationDecorator( Case.getCaseCaption = function (rawCase) { let caseCaption; switch (rawCase.partyType) { - case ContactFactory.PARTY_TYPES.corporation: - case ContactFactory.PARTY_TYPES.petitioner: + case PARTY_TYPES.corporation: + case PARTY_TYPES.petitioner: caseCaption = `${rawCase.contactPrimary.name}, Petitioner`; break; - case ContactFactory.PARTY_TYPES.petitionerSpouse: + case PARTY_TYPES.petitionerSpouse: caseCaption = `${rawCase.contactPrimary.name} & ${rawCase.contactSecondary.name}, Petitioners`; break; - case ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse: + case PARTY_TYPES.petitionerDeceasedSpouse: caseCaption = `${rawCase.contactPrimary.name} & ${rawCase.contactSecondary.name}, Deceased, ${rawCase.contactPrimary.name}, Surviving Spouse, Petitioners`; break; - case ContactFactory.PARTY_TYPES.estate: + case PARTY_TYPES.estate: caseCaption = `Estate of ${rawCase.contactPrimary.name}, Deceased, ${rawCase.contactPrimary.secondaryName}, ${rawCase.contactPrimary.title}, Petitioner(s)`; break; - case ContactFactory.PARTY_TYPES.estateWithoutExecutor: + case PARTY_TYPES.estateWithoutExecutor: caseCaption = `Estate of ${rawCase.contactPrimary.name}, Deceased, Petitioner`; break; - case ContactFactory.PARTY_TYPES.trust: + case PARTY_TYPES.trust: caseCaption = `${rawCase.contactPrimary.name}, ${rawCase.contactPrimary.secondaryName}, Trustee, Petitioner(s)`; break; - case ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner: + case PARTY_TYPES.partnershipAsTaxMattersPartner: caseCaption = `${rawCase.contactPrimary.name}, ${rawCase.contactPrimary.secondaryName}, Tax Matters Partner, Petitioner`; break; - case ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters: + case PARTY_TYPES.partnershipOtherThanTaxMatters: caseCaption = `${rawCase.contactPrimary.name}, ${rawCase.contactPrimary.secondaryName}, A Partner Other Than the Tax Matters Partner, Petitioner`; break; - case ContactFactory.PARTY_TYPES.partnershipBBA: + case PARTY_TYPES.partnershipBBA: caseCaption = `${rawCase.contactPrimary.name}, ${rawCase.contactPrimary.secondaryName}, Partnership Representative, Petitioner(s)`; break; - case ContactFactory.PARTY_TYPES.conservator: + case PARTY_TYPES.conservator: caseCaption = `${rawCase.contactPrimary.name}, ${rawCase.contactPrimary.secondaryName}, Conservator, Petitioner`; break; - case ContactFactory.PARTY_TYPES.guardian: + case PARTY_TYPES.guardian: caseCaption = `${rawCase.contactPrimary.name}, ${rawCase.contactPrimary.secondaryName}, Guardian, Petitioner`; break; - case ContactFactory.PARTY_TYPES.custodian: + case PARTY_TYPES.custodian: caseCaption = `${rawCase.contactPrimary.name}, ${rawCase.contactPrimary.secondaryName}, Custodian, Petitioner`; break; - case ContactFactory.PARTY_TYPES.nextFriendForMinor: + case PARTY_TYPES.nextFriendForMinor: caseCaption = `${rawCase.contactPrimary.name}, Minor, ${rawCase.contactPrimary.secondaryName}, Next Friend, Petitioner`; break; - case ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson: + case PARTY_TYPES.nextFriendForIncompetentPerson: caseCaption = `${rawCase.contactPrimary.name}, Incompetent, ${rawCase.contactPrimary.secondaryName}, Next Friend, Petitioner`; break; - case ContactFactory.PARTY_TYPES.donor: + case PARTY_TYPES.donor: caseCaption = `${rawCase.contactPrimary.name}, Donor, Petitioner`; break; - case ContactFactory.PARTY_TYPES.transferee: + case PARTY_TYPES.transferee: caseCaption = `${rawCase.contactPrimary.name}, Transferee, Petitioner`; break; - case ContactFactory.PARTY_TYPES.survivingSpouse: + case PARTY_TYPES.survivingSpouse: caseCaption = `${rawCase.contactPrimary.name}, Deceased, ${rawCase.contactPrimary.secondaryName}, Surviving Spouse, Petitioner`; break; } @@ -938,6 +860,7 @@ Case.prototype.addDocument = function (document, { applicationContext }) { eventCode: document.eventCode, filedBy: document.filedBy, filingDate: document.receivedAt || document.createdAt, + numberOfPages: document.numberOfPages, }, { applicationContext }, ), @@ -955,7 +878,7 @@ Case.prototype.addDocumentWithoutDocketRecord = function (document) { Case.prototype.closeCase = function () { this.closedDate = createISODateString(); - this.status = Case.STATUS_TYPES.closed; + this.status = CASE_STATUS_TYPES.closed; this.unsetAsBlocked(); this.unsetAsHighPriority(); return this; @@ -967,7 +890,7 @@ Case.prototype.closeCase = function () { * @returns {Case} the updated case entity */ Case.prototype.markAsSentToIRS = function () { - this.status = Case.STATUS_TYPES.generalDocket; + this.status = CASE_STATUS_TYPES.generalDocket; return this; }; @@ -986,7 +909,7 @@ Case.prototype.updateCaseCaptionDocketRecord = function ({ const result = caseCaptionRegex.exec(docketRecord.description); if (result) { const [, , changedCaption] = result; - lastCaption = changedCaption.replace(` ${Case.CASE_CAPTION_POSTFIX}`, ''); + lastCaption = changedCaption.replace(` ${CASE_CAPTION_POSTFIX}`, ''); } }); @@ -997,7 +920,7 @@ Case.prototype.updateCaseCaptionDocketRecord = function ({ this.addDocketRecord( new DocketRecord( { - description: `Caption of case is amended from '${lastCaption} ${Case.CASE_CAPTION_POSTFIX}' to '${this.caseCaption} ${Case.CASE_CAPTION_POSTFIX}'`, + description: `Caption of case is amended from '${lastCaption} ${CASE_CAPTION_POSTFIX}' to '${this.caseCaption} ${CASE_CAPTION_POSTFIX}'`, eventCode: 'MINC', filingDate: createISODateString(), }, @@ -1052,14 +975,15 @@ Case.prototype.updateDocketNumberRecord = function ({ applicationContext }) { }; Case.prototype.getDocumentById = function ({ documentId }) { - return this.documents.find(document => document.documentId === documentId); + const allCaseDocuments = [...this.documents, ...this.correspondence]; + + return allCaseDocuments.find(document => document.documentId === documentId); }; Case.prototype.getPetitionDocument = function () { return this.documents.find( document => - document.documentType === - Document.INITIAL_DOCUMENT_TYPES.petition.documentType, + document.documentType === INITIAL_DOCUMENT_TYPES.petition.documentType, ); }; @@ -1090,8 +1014,7 @@ Case.prototype.setRequestForTrialDocketRecord = function ( new DocketRecord( { description: `Request for Place of Trial at ${this.preferredTrialCity}`, - eventCode: - Document.INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, + eventCode: INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, filingDate: this.receivedAt || this.createdAt, }, { applicationContext }, @@ -1161,14 +1084,17 @@ Case.prototype.updateDocketRecord = function ( /** * - * @param {Document} updatedDocument the document to update on the case + * @param {Document|Correspondence} updatedDocument the document or correspondence to update on the case * @returns {Case} the updated case entity */ Case.prototype.updateDocument = function (updatedDocument) { - const foundDocument = this.documents.find( + const allCaseDocuments = [...this.documents, ...this.correspondence]; + const foundDocument = allCaseDocuments.find( document => document.documentId === updatedDocument.documentId, ); + if (foundDocument) Object.assign(foundDocument, updatedDocument); + return this; }; @@ -1184,20 +1110,6 @@ Case.isValidCaseId = caseId => caseId, ); -/** - * isValidDocketNumber - * - * @param {string} docketNumber the docket number to validate - * @returns {*|boolean} true if the docketNumber is valid, false otherwise - */ -Case.isValidDocketNumber = docketNumber => { - return ( - docketNumber && - DOCKET_NUMBER_MATCHER.test(docketNumber) && - parseInt(docketNumber.split('-')[0]) > 100 - ); -}; - /** * stripLeadingZeros * @@ -1232,12 +1144,12 @@ Case.prototype.checkForReadyForTrial = function () { const currentDate = prepareDateFromString().toISOString(); const isCaseGeneralDocketNotAtIssue = - this.status === Case.STATUS_TYPES.generalDocket; + this.status === CASE_STATUS_TYPES.generalDocket; if (isCaseGeneralDocketNotAtIssue) { this.documents.forEach(document => { const isAnswerDocument = includes( - Case.ANSWER_DOCUMENT_CODES, + ANSWER_DOCUMENT_CODES, document.eventCode, ); @@ -1247,10 +1159,10 @@ Case.prototype.checkForReadyForTrial = function () { ); const requiredTimeElapsedSinceFiling = - daysElapsedSinceDocumentWasFiled > Case.ANSWER_CUTOFF_AMOUNT_IN_DAYS; + daysElapsedSinceDocumentWasFiled > ANSWER_CUTOFF_AMOUNT_IN_DAYS; if (isAnswerDocument && requiredTimeElapsedSinceFiling) { - this.status = Case.STATUS_TYPES.generalDocketReadyForTrial; + this.status = CASE_STATUS_TYPES.generalDocketReadyForTrial; } }); } @@ -1345,7 +1257,7 @@ Case.prototype.setAsCalendared = function (trialSessionEntity) { this.trialTime = trialSessionEntity.startTime; this.trialLocation = trialSessionEntity.trialLocation; if (trialSessionEntity.isCalendared === true) { - this.status = Case.STATUS_TYPES.calendared; + this.status = CASE_STATUS_TYPES.calendared; } return this; }; @@ -1366,7 +1278,7 @@ const isAssociatedUser = function ({ caseRaw, user }) { caseRaw.privatePractitioners && caseRaw.privatePractitioners.find(p => p.userId === user.userId); - const isIrsSuperuser = user.role === User.ROLES.irsSuperuser; + const isIrsSuperuser = user.role === ROLES.irsSuperuser; const petitionDocument = (caseRaw.documents || []).find( doc => doc.documentType === 'Petition', @@ -1387,7 +1299,7 @@ const isAssociatedUser = function ({ caseRaw, user }) { * @returns {boolean} if the case is calendared */ Case.prototype.isCalendared = function () { - return this.status === Case.STATUS_TYPES.calendared; + return this.status === CASE_STATUS_TYPES.calendared; }; /** @@ -1396,7 +1308,7 @@ Case.prototype.isCalendared = function () { * @returns {boolean} if the case is calendared */ Case.prototype.isReadyForTrial = function () { - return this.status === Case.STATUS_TYPES.generalDocketReadyForTrial; + return this.status === CASE_STATUS_TYPES.generalDocketReadyForTrial; }; /** @@ -1435,11 +1347,11 @@ Case.prototype.updateAutomaticBlocked = function ({ caseDeadlines }) { const hasPendingItems = this.doesHavePendingItems(); let automaticBlockedReason; if (hasPendingItems && !isEmpty(caseDeadlines)) { - automaticBlockedReason = Case.AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate; + automaticBlockedReason = AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate; } else if (hasPendingItems) { - automaticBlockedReason = Case.AUTOMATIC_BLOCKED_REASONS.pending; + automaticBlockedReason = AUTOMATIC_BLOCKED_REASONS.pending; } else if (!isEmpty(caseDeadlines)) { - automaticBlockedReason = Case.AUTOMATIC_BLOCKED_REASONS.dueDate; + automaticBlockedReason = AUTOMATIC_BLOCKED_REASONS.dueDate; } if (automaticBlockedReason) { this.automaticBlocked = true; @@ -1482,8 +1394,8 @@ Case.prototype.unsetAsHighPriority = function () { * @returns {Case} the updated case entity */ Case.prototype.removeFromTrial = function () { - this.status = Case.STATUS_TYPES.generalDocketReadyForTrial; - this.associatedJudge = Case.CHIEF_JUDGE; + this.status = CASE_STATUS_TYPES.generalDocketReadyForTrial; + this.associatedJudge = CHIEF_JUDGE; this.trialDate = undefined; this.trialLocation = undefined; this.trialSessionId = undefined; @@ -1530,12 +1442,12 @@ Case.prototype.setCaseStatus = function (caseStatus) { this.status = caseStatus; if ( [ - Case.STATUS_TYPES.generalDocket, - Case.STATUS_TYPES.generalDocketReadyForTrial, + CASE_STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.generalDocketReadyForTrial, ].includes(caseStatus) ) { - this.associatedJudge = Case.CHIEF_JUDGE; - } else if (caseStatus === Case.STATUS_TYPES.closed) { + this.associatedJudge = CHIEF_JUDGE; + } else if (caseStatus === CASE_STATUS_TYPES.closed) { this.closeCase(); } return this; @@ -1629,10 +1541,10 @@ Case.prototype.getConsolidationStatus = function ({ caseEntity }) { */ Case.prototype.canConsolidate = function (caseToConsolidate) { const ineligibleStatusTypes = [ - Case.STATUS_TYPES.new, - Case.STATUS_TYPES.generalDocket, - Case.STATUS_TYPES.closed, - Case.STATUS_TYPES.onAppeal, + CASE_STATUS_TYPES.new, + CASE_STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.closed, + CASE_STATUS_TYPES.onAppeal, ]; const caseToCheck = caseToConsolidate || this; @@ -1779,5 +1691,56 @@ Case.prototype.fileCorrespondence = function (correspondenceEntity) { return this; }; +/** + * adds the statistic to the list of statistics on the case + * + * @param {Statistic} statisticEntity the statistic to add to the case + * @returns {Case} this case entity + */ +Case.prototype.addStatistic = function (statisticEntity) { + if (this.statistics.length === 12) { + throw new Error('maximum number of statistics reached'); + } + + this.statistics = [...this.statistics, statisticEntity]; + + return this; +}; + +/** + * updates the statistic with the given index on the case + * + * @param {Statistic} statisticEntity the statistic to update on the case + * @param {string} statisticId the id of the statistic to update + * @returns {Case} this case entity + */ +Case.prototype.updateStatistic = function (statisticEntity, statisticId) { + const statisticToUpdate = this.statistics.find( + statistic => statistic.statisticId === statisticId, + ); + + if (statisticToUpdate) Object.assign(statisticToUpdate, statisticEntity); + + return this; +}; + +/** + * deletes the statistic with the given index from the case + * + * @param {string} statisticId the id of the statistic to delete + * @returns {Case} this case entity + */ +Case.prototype.deleteStatistic = function (statisticId) { + const statisticIndexToDelete = this.statistics.findIndex( + statistic => statistic.statisticId === statisticId, + ); + + if (statisticIndexToDelete !== -1) { + this.statistics.splice(statisticIndexToDelete, 1); + } + + return this; +}; + exports.Case = Case; exports.isAssociatedUser = isAssociatedUser; diff --git a/shared/src/business/entities/cases/Case.test.js b/shared/src/business/entities/cases/Case.test.js index 8249cca5b27..9b1c23efaeb 100644 --- a/shared/src/business/entities/cases/Case.test.js +++ b/shared/src/business/entities/cases/Case.test.js @@ -1,3 +1,15 @@ +const { + ANSWER_CUTOFF_AMOUNT_IN_DAYS, + ANSWER_CUTOFF_UNIT, + AUTOMATIC_BLOCKED_REASONS, + CASE_STATUS_TYPES, + CHIEF_JUDGE, + COUNTRY_TYPES, + INITIAL_DOCUMENT_TYPES, + PARTY_TYPES, + PAYMENT_STATUS, + ROLES, +} = require('../EntityConstants'); const { applicationContext, } = require('../../test/createTestApplicationContext'); @@ -7,15 +19,15 @@ const { } = require('../../../test/mockCase'); const { Case, isAssociatedUser } = require('./Case'); const { ContactFactory } = require('../contacts/ContactFactory'); +const { Correspondence } = require('../Correspondence'); const { DocketRecord } = require('../DocketRecord'); -const { Document } = require('../Document'); const { IrsPractitioner } = require('../IrsPractitioner'); const { MOCK_DOCUMENTS } = require('../../../test/mockDocuments'); const { MOCK_USERS } = require('../../../test/mockUsers'); const { prepareDateFromString } = require('../../utilities/DateHandler'); const { PrivatePractitioner } = require('../PrivatePractitioner'); +const { Statistic } = require('../Statistic'); const { TrialSession } = require('../trialSessions/TrialSession'); -const { User } = require('../User'); const { WorkItem } = require('../WorkItem'); describe('Case entity', () => { @@ -172,6 +184,17 @@ describe('Case entity', () => { expect(myCase.isValid()).toBeTruthy(); }); + it('Creates a valid case from an already existing case json when the docketNumber has leading zeroes', () => { + const myCase = new Case( + { ...MOCK_CASE, docketNumber: '00101-20' }, + { + applicationContext, + }, + ); + expect(myCase.isValid()).toBeTruthy(); + expect(myCase.docketNumber).toBe('101-20'); + }); + it('Creates an invalid case with an invalid nested contact object', () => { const myCase = new Case( { @@ -243,8 +266,8 @@ describe('Case entity', () => { ...MOCK_CASE, statistics: [ { - deficiencyAmount: 1, - totalPenalties: 1, + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, year: '2001', yearOrPeriod: 'Year', }, @@ -257,6 +280,19 @@ describe('Case entity', () => { expect(myCase.isValid()).toBeTruthy(); }); + it('Creates an invalid case with an invalid trial time', () => { + const myCase = new Case( + { + ...MOCK_CASE, + trialTime: '91:30', + }, + { + applicationContext, + }, + ); + expect(myCase.isValid()).toBeFalsy(); + }); + it('Creates an invalid case with blocked set to true but no blockedReason or blockedDate', () => { const myCase = new Case( { @@ -327,13 +363,26 @@ describe('Case entity', () => { expect(myCase.isValid()).toBeTruthy(); }); + it('Creates a valid case with a trial time', () => { + const myCase = new Case( + { + ...MOCK_CASE, + trialTime: '9:30', + }, + { + applicationContext, + }, + ); + expect(myCase.isValid()).toBeTruthy(); + }); + it('Creates a valid case with automaticBlocked set to true and a valid automaticBlockedReason and automaticBlockedDate', () => { const myCase = new Case( { ...MOCK_CASE, automaticBlocked: true, automaticBlockedDate: '2019-03-01T21:42:29.073Z', - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pending, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pending, }, { applicationContext, @@ -388,7 +437,7 @@ describe('Case entity', () => { const myCase = new Case( { ...MOCK_CASE, - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, { applicationContext, @@ -405,7 +454,7 @@ describe('Case entity', () => { { ...MOCK_CASE, closedDate: '2019-03-01T21:40:46.415Z', - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, { applicationContext, @@ -432,7 +481,7 @@ describe('Case entity', () => { const myCase = new Case( { ...MOCK_CASE, - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, + petitionPaymentStatus: PAYMENT_STATUS.PAID, }, { applicationContext, @@ -448,7 +497,7 @@ describe('Case entity', () => { const myCase = new Case( { ...MOCK_CASE, - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, }, { applicationContext, @@ -527,16 +576,6 @@ describe('Case entity', () => { }); }); - describe('isValidDocketNumber', () => { - it('returns true if a valid docketNumber', () => { - expect(Case.isValidDocketNumber('00101-00')).toBeTruthy(); - }); - - it('returns false if an invalid docket number', () => { - expect(Case.isValidDocketNumber('00')).toBeFalsy(); - }); - }); - describe('markAsSentToIRS', () => { it('updates case status to general docket not at issue', () => { const caseRecord = new Case( @@ -561,7 +600,7 @@ describe('Case entity', () => { }, ); caseRecord.markAsSentToIRS(); - expect(caseRecord.status).toEqual(Case.STATUS_TYPES.generalDocket); + expect(caseRecord.status).toEqual(CASE_STATUS_TYPES.generalDocket); }); }); @@ -577,7 +616,7 @@ describe('Case entity', () => { contactSecondary: { name: 'Test Petitioner 2', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseCaption).toEqual( 'Test Petitioner & Test Petitioner 2, Petitioners', @@ -590,7 +629,7 @@ describe('Case entity', () => { contactSecondary: { name: 'Test Petitioner 2', }, - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }); expect(caseCaption).toEqual( 'Test Petitioner & Test Petitioner 2, Deceased, Test Petitioner, Surviving Spouse, Petitioners', @@ -600,7 +639,7 @@ describe('Case entity', () => { it('party type Estate with an Executor/Personal Representative/Fiduciary/etc.', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -612,7 +651,7 @@ describe('Case entity', () => { it('party type Estate without an Executor/Personal Representative/Fiduciary/etc.', () => { const caseCaption = Case.getCaseCaption({ ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }); expect(caseCaption).toEqual( 'Estate of Test Petitioner, Deceased, Petitioner', @@ -622,7 +661,7 @@ describe('Case entity', () => { it('party type Trust', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -634,7 +673,7 @@ describe('Case entity', () => { it('party type Corporation', () => { const caseCaption = Case.getCaseCaption({ ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }); expect(caseCaption).toEqual('Test Petitioner, Petitioner'); }); @@ -642,7 +681,7 @@ describe('Case entity', () => { it('party type Partnership Tax Matters', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -654,7 +693,7 @@ describe('Case entity', () => { it('party type Partnership Other Than Tax Matters', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + partyType: PARTY_TYPES.partnershipOtherThanTaxMatters, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -666,7 +705,7 @@ describe('Case entity', () => { it('party type Partnership BBA', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -678,7 +717,7 @@ describe('Case entity', () => { it('party type Conservator', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.conservator, + partyType: PARTY_TYPES.conservator, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -690,7 +729,7 @@ describe('Case entity', () => { it('party type Guardian', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -702,7 +741,7 @@ describe('Case entity', () => { it('party type Custodian', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.custodian, + partyType: PARTY_TYPES.custodian, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -714,7 +753,7 @@ describe('Case entity', () => { it('party type Minor', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -726,7 +765,7 @@ describe('Case entity', () => { it('party type Legally Incompetent Person', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + partyType: PARTY_TYPES.nextFriendForIncompetentPerson, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -738,7 +777,7 @@ describe('Case entity', () => { it('party type Donor', () => { const caseCaption = Case.getCaseCaption({ ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.donor, + partyType: PARTY_TYPES.donor, }); expect(caseCaption).toEqual('Test Petitioner, Donor, Petitioner'); }); @@ -746,7 +785,7 @@ describe('Case entity', () => { it('party type Transferee', () => { const caseCaption = Case.getCaseCaption({ ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, }); expect(caseCaption).toEqual('Test Petitioner, Transferee, Petitioner'); }); @@ -754,7 +793,7 @@ describe('Case entity', () => { it('party type Surviving Spouse', () => { const mockCase = { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.survivingSpouse, + partyType: PARTY_TYPES.survivingSpouse, }; mockCase.contactPrimary.secondaryName = 'Test Petitioner 2'; const caseCaption = Case.getCaseCaption(mockCase); @@ -982,7 +1021,7 @@ describe('Case entity', () => { { docketNumber: '123-19', isPaper: false, - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, @@ -1001,7 +1040,7 @@ describe('Case entity', () => { { docketNumber: '123-19', isPaper: true, - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, @@ -1042,7 +1081,7 @@ describe('Case entity', () => { "Docket Number is amended from '123-19B' to '123-19P'", }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, @@ -1107,7 +1146,7 @@ describe('Case entity', () => { { caseCaption: 'A New Caption', initialCaption: 'Caption', - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, @@ -1159,7 +1198,7 @@ describe('Case entity', () => { }, ], initialCaption: 'Caption', - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, @@ -1186,10 +1225,10 @@ describe('Case entity', () => { ); const workItem = new WorkItem( { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', document: {}, @@ -1203,10 +1242,10 @@ describe('Case entity', () => { expect(workItems.length).toEqual(1); expect(workItems).toMatchObject([ { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', document: {}, @@ -1222,13 +1261,13 @@ describe('Case entity', () => { const caseToCheck = new Case( { documents: [], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, }, ).checkForReadyForTrial(); - expect(caseToCheck.status).toEqual(Case.STATUS_TYPES.generalDocket); + expect(caseToCheck.status).toEqual(CASE_STATUS_TYPES.generalDocket); }); it('should not change the status if an answer document has been filed, but the cutoff has not elapsed', () => { @@ -1240,13 +1279,13 @@ describe('Case entity', () => { eventCode: 'A', }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, }, ).checkForReadyForTrial(); - expect(caseToCheck.status).toEqual(Case.STATUS_TYPES.generalDocket); + expect(caseToCheck.status).toEqual(CASE_STATUS_TYPES.generalDocket); }); it('should not change the status if a non answer document has been filed before the cutoff', () => { @@ -1260,13 +1299,13 @@ describe('Case entity', () => { eventCode: 'ZZZs', }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, }, ).checkForReadyForTrial(); - expect(caseToCheck.status).toEqual(Case.STATUS_TYPES.generalDocket); + expect(caseToCheck.status).toEqual(CASE_STATUS_TYPES.generalDocket); }); it("should NOT change the status to 'Ready for Trial' when an answer document has been filed on the cutoff", () => { @@ -1281,15 +1320,12 @@ describe('Case entity', () => { documents: [ { createdAt: prepareDateFromString() - .subtract( - Case.ANSWER_CUTOFF_AMOUNT_IN_DAYS, - Case.ANSWER_CUTOFF_UNIT, - ) + .subtract(ANSWER_CUTOFF_AMOUNT_IN_DAYS, ANSWER_CUTOFF_UNIT) .toISOString(), eventCode: 'A', }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, @@ -1297,16 +1333,13 @@ describe('Case entity', () => { ).checkForReadyForTrial(); expect(caseToCheck.status).not.toEqual( - Case.STATUS_TYPES.generalDocketReadyForTrial, + CASE_STATUS_TYPES.generalDocketReadyForTrial, ); }); it("should not change the status to 'Ready for Trial' when an answer document has been filed before the cutoff but case is not 'Not at issue'", () => { const createdAt = prepareDateFromString() - .subtract( - Case.ANSWER_CUTOFF_AMOUNT_IN_DAYS + 10, - Case.ANSWER_CUTOFF_UNIT, - ) + .subtract(ANSWER_CUTOFF_AMOUNT_IN_DAYS + 10, ANSWER_CUTOFF_UNIT) .toISOString(); const caseToCheck = new Case( @@ -1317,22 +1350,19 @@ describe('Case entity', () => { eventCode: 'A', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, { applicationContext, }, ).checkForReadyForTrial(); - expect(caseToCheck.status).toEqual(Case.STATUS_TYPES.new); + expect(caseToCheck.status).toEqual(CASE_STATUS_TYPES.new); }); it("should change the status to 'Ready for Trial' when an answer document has been filed before the cutoff", () => { const createdAt = prepareDateFromString() - .subtract( - Case.ANSWER_CUTOFF_AMOUNT_IN_DAYS + 10, - Case.ANSWER_CUTOFF_UNIT, - ) + .subtract(ANSWER_CUTOFF_AMOUNT_IN_DAYS + 10, ANSWER_CUTOFF_UNIT) .toISOString(); const caseToCheck = new Case( @@ -1343,7 +1373,7 @@ describe('Case entity', () => { eventCode: 'A', }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, { applicationContext, @@ -1351,7 +1381,7 @@ describe('Case entity', () => { ).checkForReadyForTrial(); expect(caseToCheck.status).toEqual( - Case.STATUS_TYPES.generalDocketReadyForTrial, + CASE_STATUS_TYPES.generalDocketReadyForTrial, ); }); }); @@ -1516,7 +1546,7 @@ describe('Case entity', () => { trialSessionId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); expect(myCase.trialSessionId).toBeTruthy(); - expect(myCase.status).toEqual(Case.STATUS_TYPES.calendared); + expect(myCase.status).toEqual(CASE_STATUS_TYPES.calendared); }); it('should set case as calendared with all trial session fields if the trial session is calendared', () => { @@ -1543,7 +1573,7 @@ describe('Case entity', () => { ); myCase.setAsCalendared(trialSession); - expect(myCase.status).toEqual(Case.STATUS_TYPES.calendared); + expect(myCase.status).toEqual(CASE_STATUS_TYPES.calendared); expect(myCase.trialDate).toBeTruthy(); expect(myCase.associatedJudge).toBeTruthy(); expect(myCase.trialLocation).toBeTruthy(); @@ -1575,9 +1605,9 @@ describe('Case entity', () => { ); myCase.setAsCalendared(trialSession); - expect(myCase.status).toEqual(Case.STATUS_TYPES.new); + expect(myCase.status).toEqual(CASE_STATUS_TYPES.new); expect(myCase.trialDate).toBeTruthy(); - expect(myCase.associatedJudge).toEqual(Case.CHIEF_JUDGE); + expect(myCase.associatedJudge).toEqual(CHIEF_JUDGE); expect(myCase.trialLocation).toBeTruthy(); expect(myCase.trialSessionId).toBeTruthy(); expect(myCase.trialTime).toBeTruthy(); @@ -1607,7 +1637,7 @@ describe('Case entity', () => { closedDate: expect.anything(), highPriority: false, highPriorityReason: undefined, - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }); }); }); @@ -1622,6 +1652,26 @@ describe('Case entity', () => { }); expect(result.documentId).toEqual(MOCK_DOCUMENTS[0].documentId); }); + + it('should get a correspondence document by id', () => { + const mockCorrespondence = new Correspondence({ + documentId: '123-abc', + documentTitle: 'My Correspondence', + filedBy: 'Docket clerk', + }); + const myCase = new Case( + { ...MOCK_CASE, correspondence: [mockCorrespondence] }, + { + applicationContext, + }, + ); + + const result = myCase.getDocumentById({ + documentId: mockCorrespondence.documentId, + }); + + expect(result.documentId).toEqual(mockCorrespondence.documentId); + }); }); describe('getPetitionDocument', () => { @@ -1631,7 +1681,7 @@ describe('Case entity', () => { }); const result = myCase.getPetitionDocument(); expect(result.documentType).toEqual( - Document.INITIAL_DOCUMENT_TYPES.petition.documentType, + INITIAL_DOCUMENT_TYPES.petition.documentType, ); }); }); @@ -1709,16 +1759,43 @@ describe('Case entity', () => { const myCase = new Case(MOCK_CASE, { applicationContext, }); + myCase.updateDocument({ documentId: MOCK_DOCUMENTS[0].documentId, processingStatus: 'success', }); + expect( myCase.documents.find( d => d.documentId === MOCK_DOCUMENTS[0].documentId, ).processingStatus, ).toEqual('success'); }); + + it('should update a correspondence document', () => { + const mockCorrespondence = new Correspondence({ + documentId: '123-abc', + documentTitle: 'My Correspondence', + filedBy: 'Docket clerk', + }); + const myCase = new Case( + { ...MOCK_CASE, correspondence: [mockCorrespondence] }, + { + applicationContext, + }, + ); + + myCase.updateDocument({ + documentId: mockCorrespondence.documentId, + documentTitle: 'updated title', + }); + + expect( + myCase.correspondence.find( + d => d.documentId === mockCorrespondence.documentId, + ).documentTitle, + ).toEqual('updated title'); + }); }); describe('updatePrivatePractitioner', () => { @@ -1902,7 +1979,7 @@ describe('Case entity', () => { expect(caseToUpdate.automaticBlocked).toEqual(true); expect(caseToUpdate.automaticBlockedReason).toEqual( - Case.AUTOMATIC_BLOCKED_REASONS.pending, + AUTOMATIC_BLOCKED_REASONS.pending, ); expect(caseToUpdate.automaticBlockedDate).toBeDefined(); expect(caseToUpdate.isValid()).toBeTruthy(); @@ -1999,7 +2076,7 @@ describe('Case entity', () => { ); caseToUpdate.setAsCalendared(trialSession); - expect(caseToUpdate.status).toEqual(Case.STATUS_TYPES.calendared); + expect(caseToUpdate.status).toEqual(CASE_STATUS_TYPES.calendared); expect(caseToUpdate.trialDate).toBeTruthy(); expect(caseToUpdate.associatedJudge).toEqual('Judge Buch'); expect(caseToUpdate.trialLocation).toBeTruthy(); @@ -2009,10 +2086,10 @@ describe('Case entity', () => { caseToUpdate.removeFromTrial(); expect(caseToUpdate.status).toEqual( - Case.STATUS_TYPES.generalDocketReadyForTrial, + CASE_STATUS_TYPES.generalDocketReadyForTrial, ); expect(caseToUpdate.trialDate).toBeFalsy(); - expect(caseToUpdate.associatedJudge).toEqual(Case.CHIEF_JUDGE); + expect(caseToUpdate.associatedJudge).toEqual(CHIEF_JUDGE); expect(caseToUpdate.trialLocation).toBeFalsy(); expect(caseToUpdate.trialSessionId).toBeFalsy(); expect(caseToUpdate.trialTime).toBeFalsy(); @@ -2044,7 +2121,7 @@ describe('Case entity', () => { ); caseToUpdate.setAsCalendared(trialSession); - expect(caseToUpdate.status).toEqual(Case.STATUS_TYPES.calendared); + expect(caseToUpdate.status).toEqual(CASE_STATUS_TYPES.calendared); expect(caseToUpdate.trialDate).toBeTruthy(); expect(caseToUpdate.associatedJudge).toEqual('Judge Buch'); expect(caseToUpdate.trialLocation).toBeTruthy(); @@ -2084,7 +2161,7 @@ describe('Case entity', () => { ); caseToUpdate.setAsCalendared(trialSession); - expect(caseToUpdate.status).toEqual(Case.STATUS_TYPES.calendared); + expect(caseToUpdate.status).toEqual(CASE_STATUS_TYPES.calendared); expect(caseToUpdate.trialDate).toBeTruthy(); expect(caseToUpdate.associatedJudge).toEqual('Judge Buch'); expect(caseToUpdate.trialLocation).toBeTruthy(); @@ -2142,10 +2219,10 @@ describe('Case entity', () => { }, ); - updatedCase.setCaseStatus(Case.STATUS_TYPES.generalDocket); + updatedCase.setCaseStatus(CASE_STATUS_TYPES.generalDocket); - expect(updatedCase.status).toEqual(Case.STATUS_TYPES.generalDocket); - expect(updatedCase.associatedJudge).toEqual(Case.CHIEF_JUDGE); + expect(updatedCase.status).toEqual(CASE_STATUS_TYPES.generalDocket); + expect(updatedCase.associatedJudge).toEqual(CHIEF_JUDGE); }); it('should update the case status, leave the associated judge unchanged, and call closeCase if the new status is Closed', () => { @@ -2161,9 +2238,9 @@ describe('Case entity', () => { }, ); - updatedCase.setCaseStatus(Case.STATUS_TYPES.closed); + updatedCase.setCaseStatus(CASE_STATUS_TYPES.closed); - expect(updatedCase.status).toEqual(Case.STATUS_TYPES.closed); + expect(updatedCase.status).toEqual(CASE_STATUS_TYPES.closed); expect(updatedCase.associatedJudge).toEqual('Judge Buch'); expect(closeCaseSpy).toBeCalled(); closeCaseSpy.mockRestore(); @@ -2191,7 +2268,7 @@ describe('Case entity', () => { const contactPrimary = { address1: '123 Main St', city: 'Somewhere', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, name: 'Test Petitioner', postalCode: '12345', state: 'TN', @@ -2201,7 +2278,7 @@ describe('Case entity', () => { const contactSecondary = { address1: '123 Main St', city: 'Somewhere', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, name: 'Contact Secondary', postalCode: '12345', state: 'TN', @@ -2227,7 +2304,7 @@ describe('Case entity', () => { contactPrimary, contactSecondary, irsPractitioners, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, privatePractitioners, }, { @@ -2251,7 +2328,7 @@ describe('Case entity', () => { contactPrimary, contactSecondary, irsPractitioners, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, privatePractitioners, }, { @@ -2795,7 +2872,7 @@ describe('Case entity', () => { const isAssociated = isAssociatedUser({ caseRaw: caseEntity.toRawObject(), user: { - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: '098d5055-dd90-42af-aec9-056a9843a7e0', }, }); @@ -2814,7 +2891,7 @@ describe('Case entity', () => { const isAssociated = isAssociatedUser({ caseRaw: caseEntity.toRawObject(), user: { - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: '098d5055-dd90-42af-aec9-056a9843a7e0', }, }); @@ -2828,7 +2905,7 @@ describe('Case entity', () => { const isAssociated = isAssociatedUser({ caseRaw: caseEntity, user: { - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: '098d5055-dd90-42af-aec9-056a9843a7e0', }, }); @@ -2899,7 +2976,7 @@ describe('Case entity', () => { }); describe('Statistics', () => { - it('should be required for deficiency cases', () => { + it('should be required for deficiency cases when hasVerifiedIrsNotice is true', () => { applicationContext.getCurrentUser.mockReturnValue( MOCK_USERS['a7d90c05-f6cd-442c-a168-202db587f16f'], ); @@ -2920,7 +2997,7 @@ describe('Case entity', () => { }); }); - it('should be required for deficiency cases', () => { + it('should not be required for deficiency cases when hasVerifiedIrsNotice is false', () => { applicationContext.getCurrentUser.mockReturnValue( MOCK_USERS['a7d90c05-f6cd-442c-a168-202db587f16f'], ); @@ -2969,4 +3046,218 @@ describe('Case entity', () => { expect(caseEntity.correspondence.length).toEqual(1); }); }); + + describe('statistics', () => { + it('should successfully add a statistic', () => { + const caseEntity = new Case(MOCK_CASE, { applicationContext }); + + const statisticToAdd = new Statistic( + { + determinationDeficiencyAmount: 567, + determinationTotalPenalties: 789, + irsDeficiencyAmount: 11.2, + irsTotalPenalties: 66.87, + year: 2012, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); + + caseEntity.addStatistic(statisticToAdd); + + expect(caseEntity.statistics.length).toEqual(1); + }); + + it('should throw an error if the max number of statistics for a case has already been reached', () => { + const statisticsWithMaxLength = new Array(12); // 12 is the maximum number of statistics + const caseEntity = new Case( + { + ...MOCK_CASE, + statistics: statisticsWithMaxLength, + }, + { applicationContext }, + ); + + const statisticToAdd = new Statistic( + { + determinationDeficiencyAmount: 567, + determinationTotalPenalties: 789, + irsDeficiencyAmount: 11.2, + irsTotalPenalties: 66.87, + year: 2012, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); + + let error; + try { + caseEntity.addStatistic(statisticToAdd); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.toString()).toEqual( + 'Error: maximum number of statistics reached', + ); + expect(caseEntity.statistics.length).toEqual(12); + }); + + it('should successfully update a statistic', () => { + const statisticId = '2db9f2b6-d65b-4f71-8ddc-c218d0787e15'; + + const caseEntity = new Case( + { + ...MOCK_CASE, + statistics: [ + { + determinationDeficiencyAmount: 567, + determinationTotalPenalties: 789, + irsDeficiencyAmount: 11.2, + irsTotalPenalties: 66.87, + statisticId, + year: 2012, + yearOrPeriod: 'Year', + }, + ], + }, + { applicationContext }, + ); + + const statisticToUpdate = new Statistic( + { + determinationDeficiencyAmount: 1, + determinationTotalPenalties: 1, + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + statisticId, + year: 2012, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); + + caseEntity.updateStatistic(statisticToUpdate, statisticId); + + expect(caseEntity.statistics.length).toEqual(1); + expect(caseEntity.statistics[0]).toEqual(statisticToUpdate); + }); + + it('should not update a statistic if its id is not present on the case', () => { + const originalStatistic = { + determinationDeficiencyAmount: 567, + determinationTotalPenalties: 789, + irsDeficiencyAmount: 11.2, + irsTotalPenalties: 66.87, + statisticId: '2db9f2b6-d65b-4f71-8ddc-c218d0787e15', + year: 2012, + yearOrPeriod: 'Year', + }; + + const caseEntity = new Case( + { + ...MOCK_CASE, + statistics: [originalStatistic], + }, + { applicationContext }, + ); + + const statisticToUpdate = new Statistic( + { + determinationDeficiencyAmount: 1, + determinationTotalPenalties: 1, + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + statisticId: '9f23dac6-4a9d-4e66-aafc-b6d3c892d907', + year: 2012, + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); + + caseEntity.updateStatistic( + statisticToUpdate, + '9f23dac6-4a9d-4e66-aafc-b6d3c892d907', + ); + + expect(caseEntity.statistics.length).toEqual(1); + expect(caseEntity.statistics[0]).toMatchObject(originalStatistic); + }); + + it('should successfully delete a statistic', () => { + const statistic0Id = 'cc0f6102-3537-4047-b951-74c21b1aab76'; + const statistic1Id = 'f4c00a75-f6d9-4e63-9cc7-ca1deee8a949'; + const originalStatistics = [ + { + determinationDeficiencyAmount: 1, + determinationTotalPenalties: 1, + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + statisticId: statistic0Id, + year: 2012, + yearOrPeriod: 'Year', + }, + { + determinationDeficiencyAmount: 2, + determinationTotalPenalties: 2, + irsDeficiencyAmount: 2, + irsTotalPenalties: 2, + statisticId: statistic1Id, + year: 2013, + yearOrPeriod: 'Year', + }, + ]; + + const caseEntity = new Case( + { + ...MOCK_CASE, + statistics: originalStatistics, + }, + { applicationContext }, + ); + + caseEntity.deleteStatistic(statistic0Id); + + expect(caseEntity.statistics.length).toEqual(1); + expect(caseEntity.statistics[0].statisticId).toEqual(statistic1Id); + }); + + it('should not delete a statistic if its statisticId is not present on the case', () => { + const statistic0Id = 'cc0f6102-3537-4047-b951-74c21b1aab76'; + const statistic1Id = 'f4c00a75-f6d9-4e63-9cc7-ca1deee8a949'; + const originalStatistics = [ + { + determinationDeficiencyAmount: 1, + determinationTotalPenalties: 1, + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + statisticId: statistic0Id, + year: 2012, + yearOrPeriod: 'Year', + }, + { + determinationDeficiencyAmount: 2, + determinationTotalPenalties: 2, + irsDeficiencyAmount: 2, + irsTotalPenalties: 2, + statisticId: statistic1Id, + year: 2013, + yearOrPeriod: 'Year', + }, + ]; + + const caseEntity = new Case( + { + ...MOCK_CASE, + statistics: originalStatistics, + }, + { applicationContext }, + ); + + caseEntity.deleteStatistic('16fc02bc-f00a-453c-a19c-e5597a8850ba'); + + expect(caseEntity.statistics.length).toEqual(2); + }); + }); }); diff --git a/shared/src/business/entities/cases/CaseConstants.js b/shared/src/business/entities/cases/CaseConstants.js deleted file mode 100644 index abf4245c6f3..00000000000 --- a/shared/src/business/entities/cases/CaseConstants.js +++ /dev/null @@ -1,13 +0,0 @@ -const SERVICE_INDICATOR_TYPES = { - SI_ELECTRONIC: 'Electronic', - SI_NONE: 'None', - SI_PAPER: 'Paper', -}; - -exports.SERVICE_INDICATOR_TYPES = SERVICE_INDICATOR_TYPES; - -exports.DOCKET_NUMBER_MATCHER = /^(\d{3,5}-\d{2})$/; - -exports.TRIAL_LOCATION_MATCHER = /^[a-zA-Z ]+, [a-zA-Z ]+, [0-9]+$/; - -exports.CHIEF_JUDGE = 'Chief Judge'; diff --git a/shared/src/business/entities/cases/CaseExternal.js b/shared/src/business/entities/cases/CaseExternal.js index 05dca925991..4de10f9f198 100644 --- a/shared/src/business/entities/cases/CaseExternal.js +++ b/shared/src/business/entities/cases/CaseExternal.js @@ -2,11 +2,9 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -const { - MAX_FILE_SIZE_BYTES, -} = require('../../../persistence/s3/getUploadPolicy'); const { Case } = require('./Case'); const { ContactFactory } = require('../contacts/ContactFactory'); +const { MAX_FILE_SIZE_BYTES } = require('../EntityConstants'); /** * CaseExternal Entity diff --git a/shared/src/business/entities/cases/CaseExternal.test.js b/shared/src/business/entities/cases/CaseExternal.test.js index 004b09a3452..099e541f461 100644 --- a/shared/src/business/entities/cases/CaseExternal.test.js +++ b/shared/src/business/entities/cases/CaseExternal.test.js @@ -1,8 +1,5 @@ -const { - MAX_FILE_SIZE_BYTES, -} = require('../../../persistence/s3/getUploadPolicy'); const { CaseExternal } = require('./CaseExternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { MAX_FILE_SIZE_BYTES, PARTY_TYPES } = require('../EntityConstants'); const { VALIDATION_ERROR_MESSAGES } = CaseExternal; @@ -10,7 +7,7 @@ describe('CaseExternal entity', () => { describe('isValid', () => { it('requires ownership disclosure if filing type is a business', () => { const caseExternal = new CaseExternal({ - businessType: ContactFactory.PARTY_TYPES.corporation, + businessType: PARTY_TYPES.corporation, caseType: 'Other', filingType: 'A business', hasIrsNotice: false, @@ -46,7 +43,7 @@ describe('CaseExternal entity', () => { }); it('requires stinFile', () => { const caseExternal = new CaseExternal({ - businessType: ContactFactory.PARTY_TYPES.corporation, + businessType: PARTY_TYPES.corporation, caseType: 'Other', filingType: 'A business', hasIrsNotice: false, @@ -65,7 +62,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, petitionFile: new File([], 'test.pdf'), petitionFileSize: MAX_FILE_SIZE_BYTES + 5, preferredTrialCity: 'Chattanooga, Tennessee', @@ -81,7 +78,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, petitionFile: {}, petitionFileSize: 0, preferredTrialCity: 'Chattanooga, Tennessee', @@ -97,7 +94,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', }); @@ -111,7 +108,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, petitionFile: new File([], 'testPetitionFile.pdf'), preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', @@ -128,7 +125,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', stinFile: new File([], 'test.pdf'), @@ -144,7 +141,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', stinFile: new File([], 'test.pdf'), @@ -160,7 +157,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', }); @@ -174,7 +171,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', stinFile: new File([], 'testStinFile.pdf'), @@ -193,7 +190,7 @@ describe('CaseExternal entity', () => { hasIrsNotice: true, ownershipDisclosureFile: new File([], 'odsFile.pdf'), ownershipDisclosureFileSize: MAX_FILE_SIZE_BYTES + 5, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', }); @@ -211,7 +208,7 @@ describe('CaseExternal entity', () => { hasIrsNotice: true, ownershipDisclosureFile: new File([], 'test.pdf'), ownershipDisclosureFileSize: 0, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', }); @@ -225,7 +222,7 @@ describe('CaseExternal entity', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', }); @@ -240,7 +237,7 @@ describe('CaseExternal entity', () => { filingType: 'Myself', hasIrsNotice: true, ownershipDisclosureFile: new File([], 'testStinFile.pdf'), - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', }); diff --git a/shared/src/business/entities/cases/CaseExternalForCorporationContacts.test.js b/shared/src/business/entities/cases/CaseExternalForCorporationContacts.test.js index f926048b2da..d5526e2cc22 100644 --- a/shared/src/business/entities/cases/CaseExternalForCorporationContacts.test.js +++ b/shared/src/business/entities/cases/CaseExternalForCorporationContacts.test.js @@ -1,5 +1,5 @@ const { CaseExternal } = require('./CaseExternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); describe('CaseExternal', () => { describe('for Corporation Contacts', () => { @@ -9,7 +9,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -38,7 +38,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -66,7 +66,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', diff --git a/shared/src/business/entities/cases/CaseExternalForEstateWithoutExecutorContacts.test.js b/shared/src/business/entities/cases/CaseExternalForEstateWithoutExecutorContacts.test.js index aed0ca0512d..d34b949273a 100644 --- a/shared/src/business/entities/cases/CaseExternalForEstateWithoutExecutorContacts.test.js +++ b/shared/src/business/entities/cases/CaseExternalForEstateWithoutExecutorContacts.test.js @@ -1,5 +1,5 @@ const { CaseExternal } = require('./CaseExternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); describe('CaseExternal', () => { describe('for Estate without an Executor/Personal Representative/Fiduciary/etc. Contacts', () => { @@ -9,7 +9,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -38,7 +38,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -68,7 +68,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', diff --git a/shared/src/business/entities/cases/CaseExternalForInternationalContacts.test.js b/shared/src/business/entities/cases/CaseExternalForInternationalContacts.test.js index 4b0b3ed2b3d..f6b5a25989b 100644 --- a/shared/src/business/entities/cases/CaseExternalForInternationalContacts.test.js +++ b/shared/src/business/entities/cases/CaseExternalForInternationalContacts.test.js @@ -1,5 +1,6 @@ const { CaseExternal } = require('./CaseExternal'); const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); const contactErrorMessages = ContactFactory.INTERNATIONAL_VALIDATION_ERROR_MESSAGES; @@ -22,7 +23,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -53,7 +54,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', diff --git a/shared/src/business/entities/cases/CaseExternalForMinorWithoutGuardianContacts.test.js b/shared/src/business/entities/cases/CaseExternalForMinorWithoutGuardianContacts.test.js index 66f62959be7..b68d650afe9 100644 --- a/shared/src/business/entities/cases/CaseExternalForMinorWithoutGuardianContacts.test.js +++ b/shared/src/business/entities/cases/CaseExternalForMinorWithoutGuardianContacts.test.js @@ -1,5 +1,5 @@ const { CaseExternal } = require('./CaseExternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); describe('CaseExternal', () => { describe('for Minor without Guardian Contacts', () => { @@ -9,7 +9,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -39,7 +39,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', diff --git a/shared/src/business/entities/cases/CaseExternalForPartnershipTaxMattersPartnerContacts.test.js b/shared/src/business/entities/cases/CaseExternalForPartnershipTaxMattersPartnerContacts.test.js index 1bc7a871594..f8adf764cb6 100644 --- a/shared/src/business/entities/cases/CaseExternalForPartnershipTaxMattersPartnerContacts.test.js +++ b/shared/src/business/entities/cases/CaseExternalForPartnershipTaxMattersPartnerContacts.test.js @@ -1,5 +1,5 @@ const { CaseExternal } = require('./CaseExternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); describe('CaseExternal', () => { describe('for Partnership (as the Tax Matters Partner) Contacts', () => { @@ -9,7 +9,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -39,7 +39,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', diff --git a/shared/src/business/entities/cases/CaseExternalForPetitionerAndDeceasedSpouseContacts.test.js b/shared/src/business/entities/cases/CaseExternalForPetitionerAndDeceasedSpouseContacts.test.js index 04656c13c21..dce71470cf0 100644 --- a/shared/src/business/entities/cases/CaseExternalForPetitionerAndDeceasedSpouseContacts.test.js +++ b/shared/src/business/entities/cases/CaseExternalForPetitionerAndDeceasedSpouseContacts.test.js @@ -1,5 +1,5 @@ const { CaseExternal } = require('./CaseExternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); describe('CaseExternal', () => { describe('for Petitioner And Deceased Spouse Contacts', () => { @@ -9,7 +9,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -47,7 +47,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', diff --git a/shared/src/business/entities/cases/CaseExternalForPetitionerAndSpouseContacts.test.js b/shared/src/business/entities/cases/CaseExternalForPetitionerAndSpouseContacts.test.js index 85d830afb6a..2943d5a7767 100644 --- a/shared/src/business/entities/cases/CaseExternalForPetitionerAndSpouseContacts.test.js +++ b/shared/src/business/entities/cases/CaseExternalForPetitionerAndSpouseContacts.test.js @@ -1,5 +1,5 @@ const { CaseExternal } = require('./CaseExternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); describe('CaseExternal', () => { describe('for Petitioner And Spouse Contacts', () => { @@ -9,7 +9,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -48,7 +48,7 @@ describe('CaseExternal', () => { filingType: 'Myself', hasIrsNotice: true, irsNoticeDate: '2009-10-13', - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', diff --git a/shared/src/business/entities/cases/CaseExternalIncomplete.test.js b/shared/src/business/entities/cases/CaseExternalIncomplete.test.js index 82cba162f6e..7d3110053de 100644 --- a/shared/src/business/entities/cases/CaseExternalIncomplete.test.js +++ b/shared/src/business/entities/cases/CaseExternalIncomplete.test.js @@ -1,5 +1,5 @@ const { CaseExternalIncomplete } = require('./CaseExternalIncomplete'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); describe('CaseExternalIncomplete entity', () => { describe('isValid', () => { @@ -22,7 +22,7 @@ describe('CaseExternalIncomplete entity', () => { filingType: 'Myself', hasIrsNotice: false, irsNoticeDate: null, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFileId: '102e29fa-bb8c-43ff-b18f-ddce9089dd80', preferredTrialCity: 'Chattanooga, Tennessee', procedureType: 'Small', diff --git a/shared/src/business/entities/cases/CaseExternalInformationFactory.test.js b/shared/src/business/entities/cases/CaseExternalInformationFactory.test.js index d5b5e230207..dc311509550 100644 --- a/shared/src/business/entities/cases/CaseExternalInformationFactory.test.js +++ b/shared/src/business/entities/cases/CaseExternalInformationFactory.test.js @@ -1,10 +1,8 @@ const { CaseExternalInformationFactory, } = require('./CaseExternalInformationFactory'); -const { - MAX_FILE_SIZE_BYTES, -} = require('../../../persistence/s3/getUploadPolicy'); const { ContactFactory } = require('../contacts/ContactFactory'); +const { MAX_FILE_SIZE_BYTES, PARTY_TYPES } = require('../EntityConstants'); const caseExternalErrorMessages = CaseExternalInformationFactory.VALIDATION_ERROR_MESSAGES; @@ -150,7 +148,7 @@ describe('CaseExternalInformationFactory entity', () => { name: 'Something', }, hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, stinFile: new File([], 'test.pdf'), @@ -261,7 +259,7 @@ describe('CaseExternalInformationFactory entity', () => { caseType: 'Deficiency', filingType: 'A business', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, stinFile: new File([], 'test.pdf'), @@ -278,7 +276,7 @@ describe('CaseExternalInformationFactory entity', () => { caseType: 'Deficiency', filingType: 'something else', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, stinFile: new File([], 'test.pdf'), @@ -295,7 +293,7 @@ describe('CaseExternalInformationFactory entity', () => { caseType: 'Deficiency', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, stinFile: new File([], 'test.pdf'), @@ -320,7 +318,7 @@ describe('CaseExternalInformationFactory entity', () => { caseType: 'Deficiency', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, stinFile: new File([], 'test.pdf'), @@ -403,7 +401,7 @@ describe('CaseExternalInformationFactory entity', () => { caseExternal = new CaseExternalInformationFactory({ filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, petitionFile: new File([], 'test.pdf'), stinFile: new File([], 'test.pdf'), wizardStep: '4', @@ -458,7 +456,7 @@ describe('CaseExternalInformationFactory entity', () => { }, filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, preferredTrialCity: 'Boise, Idaho', diff --git a/shared/src/business/entities/cases/CaseInternal.js b/shared/src/business/entities/cases/CaseInternal.js index fd27d4348f0..fb4fb277901 100644 --- a/shared/src/business/entities/cases/CaseInternal.js +++ b/shared/src/business/entities/cases/CaseInternal.js @@ -1,17 +1,20 @@ const joi = require('@hapi/joi'); +const { + DEFAULT_PROCEDURE_TYPE, + MAX_FILE_SIZE_BYTES, + PARTY_TYPES, + PAYMENT_STATUS, +} = require('../EntityConstants'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -const { - MAX_FILE_SIZE_BYTES, -} = require('../../../persistence/s3/getUploadPolicy'); const { Case } = require('./Case'); const { ContactFactory } = require('../contacts/ContactFactory'); const { getTimestampSchema } = require('../../../utilities/dateSchema'); const { Statistic } = require('../Statistic'); const joiStrictTimestamp = getTimestampSchema(); -CaseInternal.DEFAULT_PROCEDURE_TYPE = Case.PROCEDURE_TYPES[0]; +CaseInternal.DEFAULT_PROCEDURE_TYPE = DEFAULT_PROCEDURE_TYPE; /** * CaseInternal Entity @@ -20,7 +23,11 @@ CaseInternal.DEFAULT_PROCEDURE_TYPE = Case.PROCEDURE_TYPES[0]; * @param {object} rawCase the raw case data * @constructor */ -function CaseInternal(rawCase) { +function CaseInternal(rawCase, { applicationContext }) { + if (!applicationContext) { + throw new TypeError('applicationContext must be defined'); + } + this.applicationForWaiverOfFilingFeeFile = rawCase.applicationForWaiverOfFilingFeeFile; this.applicationForWaiverOfFilingFeeFileSize = @@ -64,7 +71,9 @@ function CaseInternal(rawCase) { this.useSameAsPrimary = rawCase.useSameAsPrimary; this.statistics = Array.isArray(rawCase.statistics) - ? rawCase.statistics.map(statistic => new Statistic(statistic)) + ? rawCase.statistics.map( + statistic => new Statistic(statistic, { applicationContext }), + ) : []; const contacts = ContactFactory.createContacts({ @@ -104,7 +113,7 @@ const paperRequirements = joi .object() .keys({ applicationForWaiverOfFilingFeeFile: joi.when('petitionPaymentStatus', { - is: Case.PAYMENT_STATUS.WAIVED, + is: PAYMENT_STATUS.WAIVED, otherwise: joi.optional().allow(null), then: joi.object().required(), }), @@ -119,26 +128,26 @@ const paperRequirements = joi caseCaption: joi.string().required(), caseType: joi.string().required(), hasVerifiedIrsNotice: joi.boolean().required(), - irsNoticeDate: Case.validationRules.irsNoticeDate, + irsNoticeDate: Case.VALIDATION_RULES.irsNoticeDate, mailingDate: joi.string().max(25).required(), - noticeOfAttachments: Case.validationRules.noticeOfAttachments, + noticeOfAttachments: Case.VALIDATION_RULES.noticeOfAttachments, orderDesignatingPlaceOfTrial: - Case.validationRules.orderDesignatingPlaceOfTrial, - orderForAmendedPetition: Case.validationRules.orderForAmendedPetition, + Case.VALIDATION_RULES.orderDesignatingPlaceOfTrial, + orderForAmendedPetition: Case.VALIDATION_RULES.orderForAmendedPetition, orderForAmendedPetitionAndFilingFee: - Case.validationRules.orderForAmendedPetitionAndFilingFee, - orderForFilingFee: Case.validationRules.orderForFilingFee, - orderForOds: Case.validationRules.orderForOds, - orderForRatification: Case.validationRules.orderForRatification, - orderToShowCause: Case.validationRules.orderToShowCause, + Case.VALIDATION_RULES.orderForAmendedPetitionAndFilingFee, + orderForFilingFee: Case.VALIDATION_RULES.orderForFilingFee, + orderForOds: Case.VALIDATION_RULES.orderForOds, + orderForRatification: Case.VALIDATION_RULES.orderForRatification, + orderToShowCause: Case.VALIDATION_RULES.orderToShowCause, ownershipDisclosureFile: joi.when('partyType', { is: joi .exist() .valid( - ContactFactory.PARTY_TYPES.corporation, - ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, - ContactFactory.PARTY_TYPES.partnershipBBA, - ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + PARTY_TYPES.corporation, + PARTY_TYPES.partnershipAsTaxMattersPartner, + PARTY_TYPES.partnershipBBA, + PARTY_TYPES.partnershipOtherThanTaxMatters, ), otherwise: joi.optional().allow(null), then: joi.when('orderForOds', { @@ -160,13 +169,13 @@ const paperRequirements = joi then: joi.number().required().min(1).max(MAX_FILE_SIZE_BYTES).integer(), }), petitionPaymentDate: joi.when('petitionPaymentStatus', { - is: Case.PAYMENT_STATUS.PAID, + is: PAYMENT_STATUS.PAID, otherwise: joiStrictTimestamp.optional().allow(null), then: joiStrictTimestamp.max('now').required(), }), - petitionPaymentMethod: Case.validationRules.petitionPaymentMethod, - petitionPaymentStatus: Case.validationRules.petitionPaymentStatus, - petitionPaymentWaivedDate: Case.validationRules.petitionPaymentWaivedDate, + petitionPaymentMethod: Case.VALIDATION_RULES.petitionPaymentMethod, + petitionPaymentStatus: Case.VALIDATION_RULES.petitionPaymentStatus, + petitionPaymentWaivedDate: Case.VALIDATION_RULES.petitionPaymentWaivedDate, preferredTrialCity: joi .alternatives() .conditional('requestForPlaceOfTrialFile', { @@ -188,14 +197,14 @@ const paperRequirements = joi otherwise: joi.optional().allow(null), then: joi.number().required().min(1).max(MAX_FILE_SIZE_BYTES).integer(), }), - statistics: Case.validationRules.statistics, + statistics: Case.VALIDATION_RULES.statistics, stinFile: joi.object().optional(), stinFileSize: joi.when('stinFile', { is: joi.exist().not(null), otherwise: joi.optional().allow(null), then: joi.number().required().min(1).max(MAX_FILE_SIZE_BYTES).integer(), }), - useSameAsPrimary: Case.validationRules.useSameAsPrimary, + useSameAsPrimary: Case.VALIDATION_RULES.useSameAsPrimary, }) .or( 'preferredTrialCity', diff --git a/shared/src/business/entities/cases/CaseInternal.test.js b/shared/src/business/entities/cases/CaseInternal.test.js index ca4522a692f..129cc4a19db 100644 --- a/shared/src/business/entities/cases/CaseInternal.test.js +++ b/shared/src/business/entities/cases/CaseInternal.test.js @@ -1,13 +1,16 @@ -const { Case } = require('./Case'); +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); const { CaseInternal } = require('./CaseInternal'); -const { ContactFactory } = require('../contacts/ContactFactory'); +const { PARTY_TYPES } = require('../EntityConstants'); +const { PAYMENT_STATUS } = require('../EntityConstants'); const { VALIDATION_ERROR_MESSAGES } = CaseInternal; describe('CaseInternal entity', () => { describe('validation', () => { it('returns the expected set of errors for an empty object', () => { - const caseInternal = new CaseInternal({}); + const caseInternal = new CaseInternal({}, { applicationContext }); expect(caseInternal.getFormattedValidationErrors()).toEqual({ caseCaption: VALIDATION_ERROR_MESSAGES.caseCaption, caseType: VALIDATION_ERROR_MESSAGES.caseType, @@ -22,120 +25,135 @@ describe('CaseInternal entity', () => { }); it('creates a valid petition with minimal information', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - caseType: 'Other', - contactPrimary: { - address1: '876 12th Ave', - city: 'Nashville', - country: 'USA', - countryType: 'domestic', - email: 'someone@example.com', - name: 'Jimmy Dean', - phone: '1234567890', - postalCode: '05198', - state: 'AK', - }, - mailingDate: 'test', - partyType: ContactFactory.PARTY_TYPES.petitioner, - petitionFile: { anObject: true }, - petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, - preferredTrialCity: 'Boise, Idaho', - procedureType: 'Small', - receivedAt: new Date().toISOString(), - requestForPlaceOfTrialFile: { anObject: true }, - requestForPlaceOfTrialFileSize: 1, - statistics: [ - { - deficiencyAmount: 1, - totalPenalties: 1, - year: '2001', - yearOrPeriod: 'Year', + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + caseType: 'Other', + contactPrimary: { + address1: '876 12th Ave', + city: 'Nashville', + country: 'USA', + countryType: 'domestic', + email: 'someone@example.com', + name: 'Jimmy Dean', + phone: '1234567890', + postalCode: '05198', + state: 'AK', }, - ], - }); + mailingDate: 'test', + partyType: PARTY_TYPES.petitioner, + petitionFile: { anObject: true }, + petitionFileSize: 1, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, + preferredTrialCity: 'Boise, Idaho', + procedureType: 'Small', + receivedAt: new Date().toISOString(), + requestForPlaceOfTrialFile: { anObject: true }, + requestForPlaceOfTrialFileSize: 1, + statistics: [ + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: '2001', + yearOrPeriod: 'Year', + }, + ], + }, + { applicationContext }, + ); expect(caseInternal.getFormattedValidationErrors()).toEqual(null); expect(caseInternal.isValid()).toEqual(true); }); it('creates a valid petition with partyType Corporation and an ods file', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - caseType: 'Other', - contactPrimary: { - address1: '876 12th Ave', - city: 'Nashville', - country: 'USA', - countryType: 'domestic', - email: 'someone@example.com', - inCareOf: 'Someone', - name: 'Jimmy Dean', - phone: '1234567890', - postalCode: '05198', - state: 'AK', + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + caseType: 'Other', + contactPrimary: { + address1: '876 12th Ave', + city: 'Nashville', + country: 'USA', + countryType: 'domestic', + email: 'someone@example.com', + inCareOf: 'Someone', + name: 'Jimmy Dean', + phone: '1234567890', + postalCode: '05198', + state: 'AK', + }, + mailingDate: 'test', + orderDesignatingPlaceOfTrial: true, + ownershipDisclosureFile: { anObject: true }, + ownershipDisclosureFileSize: 1, + partyType: PARTY_TYPES.corporation, + petitionFile: { anObject: true }, + petitionFileSize: 1, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, + procedureType: 'Small', + receivedAt: new Date().toISOString(), }, - mailingDate: 'test', - orderDesignatingPlaceOfTrial: true, - ownershipDisclosureFile: { anObject: true }, - ownershipDisclosureFileSize: 1, - partyType: ContactFactory.PARTY_TYPES.corporation, - petitionFile: { anObject: true }, - petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, - procedureType: 'Small', - receivedAt: new Date().toISOString(), - }); + { applicationContext }, + ); expect(caseInternal.getFormattedValidationErrors()).toEqual(null); expect(caseInternal.isValid()).toEqual(true); }); it('creates a valid petition with partyType Corporation and an order for ods instead of an ods file', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - caseType: 'Other', - contactPrimary: { - address1: '876 12th Ave', - city: 'Nashville', - country: 'USA', - countryType: 'domestic', - email: 'someone@example.com', - inCareOf: 'Someone', - name: 'Jimmy Dean', - phone: '1234567890', - postalCode: '05198', - state: 'AK', + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + caseType: 'Other', + contactPrimary: { + address1: '876 12th Ave', + city: 'Nashville', + country: 'USA', + countryType: 'domestic', + email: 'someone@example.com', + inCareOf: 'Someone', + name: 'Jimmy Dean', + phone: '1234567890', + postalCode: '05198', + state: 'AK', + }, + mailingDate: 'test', + orderDesignatingPlaceOfTrial: true, + orderForOds: true, + partyType: PARTY_TYPES.corporation, + petitionFile: { anObject: true }, + petitionFileSize: 1, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, + procedureType: 'Small', + receivedAt: new Date().toISOString(), }, - mailingDate: 'test', - orderDesignatingPlaceOfTrial: true, - orderForOds: true, - partyType: ContactFactory.PARTY_TYPES.corporation, - petitionFile: { anObject: true }, - petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, - procedureType: 'Small', - receivedAt: new Date().toISOString(), - }); + { applicationContext }, + ); expect(caseInternal.getFormattedValidationErrors()).toEqual(null); expect(caseInternal.isValid()).toEqual(true); }); it('fails validation if date cannot be in the future.', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - petitionFile: { anObject: true }, - petitionFileSize: 1, - receivedAt: new Date(Date.parse('9999-01-01')).toISOString(), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + petitionFile: { anObject: true }, + petitionFileSize: 1, + receivedAt: new Date(Date.parse('9999-01-01')).toISOString(), + }, + { applicationContext }, + ); expect(caseInternal.getFormattedValidationErrors()).not.toEqual(null); }); it('fails validation if petitionFile is set, but petitionFileSize is not', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - petitionFile: new File([], 'test.pdf'), - receivedAt: new Date().toISOString(), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + petitionFile: new File([], 'test.pdf'), + receivedAt: new Date().toISOString(), + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors().petitionFileSize, @@ -143,11 +161,14 @@ describe('CaseInternal entity', () => { }); it('fails validation if petitionPaymentStatus is Waived but applicationForWaiverOfFilingFeeFile is not set', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, - receivedAt: new Date().toISOString(), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, + receivedAt: new Date().toISOString(), + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors() @@ -156,9 +177,12 @@ describe('CaseInternal entity', () => { }); it('fails validation if partyType is Corporation and orderForOds is undefined', () => { - const caseInternal = new CaseInternal({ - partyType: ContactFactory.PARTY_TYPES.corporation, - }); + const caseInternal = new CaseInternal( + { + partyType: PARTY_TYPES.corporation, + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors().ownershipDisclosureFile, @@ -166,10 +190,13 @@ describe('CaseInternal entity', () => { }); it('fails validation if partyType is partnershipAsTaxMattersPartner and orderForOds is false', () => { - const caseInternal = new CaseInternal({ - orderForOds: false, - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, - }); + const caseInternal = new CaseInternal( + { + orderForOds: false, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors().ownershipDisclosureFile, @@ -177,11 +204,14 @@ describe('CaseInternal entity', () => { }); it('fails validation if applicationForWaiverOfFilingFeeFile is set, but applicationForWaiverOfFilingFeeFileSize is not', () => { - const caseInternal = new CaseInternal({ - applicationForWaiverOfFilingFeeFile: new File([], 'test.pdf'), - caseCaption: 'Dr. Leo Marvin, Petitioner', - receivedAt: new Date().toISOString(), - }); + const caseInternal = new CaseInternal( + { + applicationForWaiverOfFilingFeeFile: new File([], 'test.pdf'), + caseCaption: 'Dr. Leo Marvin, Petitioner', + receivedAt: new Date().toISOString(), + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors() @@ -192,11 +222,14 @@ describe('CaseInternal entity', () => { }); it('fails validation if stinFile is set, but stinFileSize is not', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - receivedAt: new Date().toISOString(), - stinFile: new File([], 'test.pdf'), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + receivedAt: new Date().toISOString(), + stinFile: new File([], 'test.pdf'), + }, + { applicationContext }, + ); expect(caseInternal.getFormattedValidationErrors().stinFileSize).toEqual( VALIDATION_ERROR_MESSAGES.stinFileSize[1], @@ -204,11 +237,14 @@ describe('CaseInternal entity', () => { }); it('fails validation if ownershipDisclosureFile is set, but ownershipDisclosureFileSize is not', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - ownershipDisclosureFile: new File([], 'test.pdf'), - receivedAt: new Date().toISOString(), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + ownershipDisclosureFile: new File([], 'test.pdf'), + receivedAt: new Date().toISOString(), + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors().ownershipDisclosureFileSize, @@ -216,11 +252,14 @@ describe('CaseInternal entity', () => { }); it('fails validation if requestForPlaceOfTrialFile is set, but requestForPlaceOfTrialFileSize is not', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - receivedAt: new Date().toISOString(), - requestForPlaceOfTrialFile: new File([], 'test.pdf'), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + receivedAt: new Date().toISOString(), + requestForPlaceOfTrialFile: new File([], 'test.pdf'), + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors() @@ -229,11 +268,14 @@ describe('CaseInternal entity', () => { }); it('fails validation if requestForPlaceOfTrialFile is set, but preferredTrialCity is not', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - receivedAt: new Date().toISOString(), - requestForPlaceOfTrialFile: new File([], 'test.pdf'), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + receivedAt: new Date().toISOString(), + requestForPlaceOfTrialFile: new File([], 'test.pdf'), + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors().preferredTrialCity, @@ -241,11 +283,14 @@ describe('CaseInternal entity', () => { }); it('fails validation if preferredTrialCity is set, but requestForPlaceOfTrialFile is not', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Guy Fieri, Petitioner', - preferredTrialCity: 'Flavortown, AR', - receivedAt: new Date().toISOString(), - }); + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Guy Fieri, Petitioner', + preferredTrialCity: 'Flavortown, AR', + receivedAt: new Date().toISOString(), + }, + { applicationContext }, + ); expect( caseInternal.getFormattedValidationErrors().requestForPlaceOfTrialFile, @@ -253,33 +298,36 @@ describe('CaseInternal entity', () => { }); it('fails validation if one of preferredTrialCity, RQT file, or orderDesignatingPlaceOfTrial is not selected', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - caseType: 'Other', - contactPrimary: { - address1: '876 12th Ave', - city: 'Nashville', - country: 'USA', - countryType: 'domestic', - email: 'someone@example.com', - inCareOf: 'Someone', - name: 'Jimmy Dean', - phone: '1234567890', - postalCode: '05198', - state: 'AK', + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + caseType: 'Other', + contactPrimary: { + address1: '876 12th Ave', + city: 'Nashville', + country: 'USA', + countryType: 'domestic', + email: 'someone@example.com', + inCareOf: 'Someone', + name: 'Jimmy Dean', + phone: '1234567890', + postalCode: '05198', + state: 'AK', + }, + mailingDate: 'test', + ownershipDisclosureFile: { anObject: true }, + ownershipDisclosureFileSize: 1, + partyType: PARTY_TYPES.corporation, + petitionFile: { anObject: true }, + petitionFileSize: 1, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, + procedureType: 'Small', + receivedAt: new Date().toISOString(), + stinFile: { anObject: true }, + stinFileSize: 1, }, - mailingDate: 'test', - ownershipDisclosureFile: { anObject: true }, - ownershipDisclosureFileSize: 1, - partyType: ContactFactory.PARTY_TYPES.corporation, - petitionFile: { anObject: true }, - petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, - procedureType: 'Small', - receivedAt: new Date().toISOString(), - stinFile: { anObject: true }, - stinFileSize: 1, - }); + { applicationContext }, + ); expect(caseInternal.isValid()).toEqual(false); expect(caseInternal.getFormattedValidationErrors()).toEqual({ chooseAtLeastOneValue: VALIDATION_ERROR_MESSAGES.chooseAtLeastOneValue, @@ -287,34 +335,37 @@ describe('CaseInternal entity', () => { }); it('fails validation if only orderDesignatingPlaceOfTrial is present and it is false', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Dr. Leo Marvin, Petitioner', - caseType: 'Other', - contactPrimary: { - address1: '876 12th Ave', - city: 'Nashville', - country: 'USA', - countryType: 'domestic', - email: 'someone@example.com', - inCareOf: 'Someone', - name: 'Jimmy Dean', - phone: '1234567890', - postalCode: '05198', - state: 'AK', + const caseInternal = new CaseInternal( + { + caseCaption: 'Dr. Leo Marvin, Petitioner', + caseType: 'Other', + contactPrimary: { + address1: '876 12th Ave', + city: 'Nashville', + country: 'USA', + countryType: 'domestic', + email: 'someone@example.com', + inCareOf: 'Someone', + name: 'Jimmy Dean', + phone: '1234567890', + postalCode: '05198', + state: 'AK', + }, + mailingDate: 'test', + orderDesignatingPlaceOfTrial: false, + ownershipDisclosureFile: { anObject: true }, + ownershipDisclosureFileSize: 1, + partyType: PARTY_TYPES.corporation, + petitionFile: { anObject: true }, + petitionFileSize: 1, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, + procedureType: 'Small', + receivedAt: new Date().toISOString(), + stinFile: { anObject: true }, + stinFileSize: 1, }, - mailingDate: 'test', - orderDesignatingPlaceOfTrial: false, - ownershipDisclosureFile: { anObject: true }, - ownershipDisclosureFileSize: 1, - partyType: ContactFactory.PARTY_TYPES.corporation, - petitionFile: { anObject: true }, - petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, - procedureType: 'Small', - receivedAt: new Date().toISOString(), - stinFile: { anObject: true }, - stinFileSize: 1, - }); + { applicationContext }, + ); expect(caseInternal.isValid()).toEqual(false); expect(caseInternal.getFormattedValidationErrors()).toEqual({ chooseAtLeastOneValue: VALIDATION_ERROR_MESSAGES.chooseAtLeastOneValue, diff --git a/shared/src/business/entities/cases/CaseSearch.js b/shared/src/business/entities/cases/CaseSearch.js index 3a0fbc382f8..42185074c8b 100644 --- a/shared/src/business/entities/cases/CaseSearch.js +++ b/shared/src/business/entities/cases/CaseSearch.js @@ -2,9 +2,7 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); - -CaseSearch.CASE_SEARCH_MIN_YEAR = 1986; -CaseSearch.CASE_SEARCH_PAGE_SIZE = 5; +const { CASE_SEARCH_MIN_YEAR } = require('../EntityConstants'); CaseSearch.validationName = 'CaseSearch'; @@ -16,7 +14,7 @@ CaseSearch.validationName = 'CaseSearch'; */ function CaseSearch(rawProps) { this.petitionerName = rawProps.petitionerName; - this.yearFiledMin = rawProps.yearFiledMin || CaseSearch.CASE_SEARCH_MIN_YEAR; + this.yearFiledMin = rawProps.yearFiledMin || CASE_SEARCH_MIN_YEAR; this.yearFiledMax = rawProps.yearFiledMax || undefined; this.petitionerState = rawProps.petitionerState || undefined; this.countryType = rawProps.countryType || undefined; diff --git a/shared/src/business/entities/cases/CaseSearch.test.js b/shared/src/business/entities/cases/CaseSearch.test.js index 66efcb86a3c..13f244518d7 100644 --- a/shared/src/business/entities/cases/CaseSearch.test.js +++ b/shared/src/business/entities/cases/CaseSearch.test.js @@ -1,7 +1,7 @@ +const { CASE_SEARCH_MIN_YEAR } = require('../EntityConstants'); const { CaseSearch } = require('./CaseSearch'); const errorMessages = CaseSearch.VALIDATION_ERROR_MESSAGES; -const minYear = CaseSearch.CASE_SEARCH_MIN_YEAR; describe('Case Search entity', () => { it('needs only a petitioner name to be valid', () => { @@ -11,7 +11,7 @@ describe('Case Search entity', () => { petitionerName: 'Solomon Grundy', petitionerState: undefined, yearFiledMax: undefined, - yearFiledMin: minYear, + yearFiledMin: CASE_SEARCH_MIN_YEAR, }); const validationErrors = caseSearch.getFormattedValidationErrors(); expect(validationErrors).toEqual(null); diff --git a/shared/src/business/entities/cases/PublicCase.js b/shared/src/business/entities/cases/PublicCase.js index 8946a625d60..8966c7536ca 100644 --- a/shared/src/business/entities/cases/PublicCase.js +++ b/shared/src/business/entities/cases/PublicCase.js @@ -1,11 +1,15 @@ const joi = require('@hapi/joi'); +const { + COURT_ISSUED_EVENT_CODES, + ORDER_TYPES, + TRANSCRIPT_EVENT_CODE, +} = require('../EntityConstants'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -const { Document } = require('../Document'); +const { compareStrings } = require('../../utilities/sortFunctions'); const { getTimestampSchema } = require('../../../utilities/dateSchema'); const { map } = require('lodash'); -const { Order } = require('../orders/Order'); const { PublicContact } = require('./PublicContact'); const { PublicDocketRecordEntry } = require('./PublicDocketRecordEntry'); const { PublicDocument } = require('./PublicDocument'); @@ -25,7 +29,9 @@ function PublicCase(rawCase, { applicationContext }) { this.createdAt = rawCase.createdAt; this.docketNumber = rawCase.docketNumber; this.docketNumberSuffix = rawCase.docketNumberSuffix; - this.docketNumberWithSuffix = rawCase.docketNumberWithSuffix; + this.docketNumberWithSuffix = + rawCase.docketNumberWithSuffix || + `${this.docketNumber}${this.docketNumberSuffix || ''}`; this.receivedAt = rawCase.receivedAt; this.isSealed = !!rawCase.sealedDate; @@ -45,7 +51,7 @@ function PublicCase(rawCase, { applicationContext }) { this.documents = (rawCase.documents || []) .map(document => new PublicDocument(document, { applicationContext })) .filter(document => !isDraftDocument(document, this.docketRecord)) - .sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1)); + .sort((a, b) => compareStrings(a.createdAt, b.createdAt)); } const publicCaseSchema = { @@ -85,9 +91,9 @@ joiValidationDecorator( ); const isDraftDocument = function (document, docketRecord) { - const orderDocumentTypes = map(Order.ORDER_TYPES, 'documentType'); + const orderDocumentTypes = map(ORDER_TYPES, 'documentType'); const courtIssuedDocumentTypes = map( - Document.COURT_ISSUED_EVENT_CODES, + COURT_ISSUED_EVENT_CODES, 'documentType', ); @@ -107,14 +113,14 @@ const isDraftDocument = function (document, docketRecord) { }; const isPrivateDocument = function (document, docketRecord) { - const orderDocumentTypes = map(Order.ORDER_TYPES, 'documentType'); + const orderDocumentTypes = map(ORDER_TYPES, 'documentType'); const courtIssuedDocumentTypes = map( - Document.COURT_ISSUED_EVENT_CODES, + COURT_ISSUED_EVENT_CODES, 'documentType', ); const isStipDecision = document.documentType === 'Stipulated Decision'; - const isTranscript = document.eventCode === Document.TRANSCRIPT_EVENT_CODE; + const isTranscript = document.eventCode === TRANSCRIPT_EVENT_CODE; const isOrder = orderDocumentTypes.includes(document.documentType); const isCourtIssuedDocument = courtIssuedDocumentTypes.includes( document.documentType, diff --git a/shared/src/business/entities/cases/PublicCase.test.js b/shared/src/business/entities/cases/PublicCase.test.js index b063dc53011..ca1e1e06378 100644 --- a/shared/src/business/entities/cases/PublicCase.test.js +++ b/shared/src/business/entities/cases/PublicCase.test.js @@ -87,6 +87,7 @@ describe('PublicCase', () => { createdAt: 'testing', docketNumber: 'testing', docketNumberSuffix: 'testing', + docketNumberWithSuffix: 'testingtesting', docketRecord: [], documents: [], isSealed: false, @@ -119,6 +120,7 @@ describe('PublicCase', () => { createdAt: 'testing', docketNumber: 'testing', docketNumberSuffix: 'testing', + docketNumberWithSuffix: 'testingtesting', docketRecord: [], documents: [], isSealed: false, @@ -159,6 +161,7 @@ describe('PublicCase', () => { createdAt: 'testing', docketNumber: 'testing', docketNumberSuffix: 'testing', + docketNumberWithSuffix: 'testingtesting', docketRecord: [ { description: undefined, @@ -332,4 +335,44 @@ describe('PublicCase', () => { expect(isPrivate).toEqual(true); }); }); + + it('should compute docketNumberWithSuffix if it is not provided', () => { + const entity = new PublicCase( + { + caseCaption: 'testing', + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + contactPrimary: {}, + contactSecondary: {}, + createdAt: '2020-01-02T03:30:45.007Z', + docketNumber: '102-20', + docketNumberSuffix: 'SL', + docketNumberWithSuffix: null, + docketRecord: [{}], + documents: [{}], + receivedAt: '2020-01-05T03:30:45.007Z', + }, + {}, + ); + expect(entity.docketNumberWithSuffix).toBe('102-20SL'); + }); + + it('should compute docketNumberWithSuffix with just docketNumber if there is no suffix', () => { + const entity = new PublicCase( + { + caseCaption: 'testing', + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + contactPrimary: {}, + contactSecondary: {}, + createdAt: '2020-01-02T03:30:45.007Z', + docketNumber: '102-20', + docketNumberSuffix: null, + docketNumberWithSuffix: null, + docketRecord: [{}], + documents: [{}], + receivedAt: '2020-01-05T03:30:45.007Z', + }, + {}, + ); + expect(entity.docketNumberWithSuffix).toBe('102-20'); + }); }); diff --git a/shared/src/business/entities/cases/PublicDocketRecordEntry.js b/shared/src/business/entities/cases/PublicDocketRecordEntry.js index 6ced543226c..2d1d2af4ab4 100644 --- a/shared/src/business/entities/cases/PublicDocketRecordEntry.js +++ b/shared/src/business/entities/cases/PublicDocketRecordEntry.js @@ -18,6 +18,7 @@ function PublicDocketRecordEntry(rawDocketEntry) { this.filedBy = rawDocketEntry.filedBy; this.index = rawDocketEntry.index; this.filingDate = rawDocketEntry.filingDate; + this.numberOfPages = rawDocketEntry.numberOfPages; } joiValidationDecorator( @@ -25,9 +26,10 @@ joiValidationDecorator( joi.object().keys({ description: joi.string().optional(), documentId: joi.string().optional(), - filedBy: joiStrictTimestamp.optional(), + filedBy: joi.string().optional(), filingDate: joiStrictTimestamp.max('now').optional(), // Required on DocketRecord so probably should be required here. index: joi.number().integer().optional(), + numberOfPages: joi.number().integer().optional(), }), {}, ); diff --git a/shared/src/business/entities/cases/PublicDocketRecordEntry.test.js b/shared/src/business/entities/cases/PublicDocketRecordEntry.test.js index dbb8be1fed2..84f1740fa17 100644 --- a/shared/src/business/entities/cases/PublicDocketRecordEntry.test.js +++ b/shared/src/business/entities/cases/PublicDocketRecordEntry.test.js @@ -6,16 +6,16 @@ describe('PublicDocketRecordEntry', () => { description: 'testing', documentId: 'testing', filedBy: 'testing', - filingDate: 'testing', - index: 'testing', + filingDate: '2020-05-27T09:23:43.007Z', + index: 1, }); - expect(entity.toRawObject()).toEqual({ + expect(entity.validate().toRawObject()).toEqual({ description: 'testing', documentId: 'testing', filedBy: 'testing', - filingDate: 'testing', - index: 'testing', + filingDate: '2020-05-27T09:23:43.007Z', + index: 1, }); }); }); diff --git a/shared/src/business/entities/contacts/ContactFactory.js b/shared/src/business/entities/contacts/ContactFactory.js index cbe384ddd9b..ef59bff24bf 100644 --- a/shared/src/business/entities/contacts/ContactFactory.js +++ b/shared/src/business/entities/contacts/ContactFactory.js @@ -1,122 +1,17 @@ const joi = require('@hapi/joi'); - +const { + COUNTRY_TYPES, + PARTY_TYPES, + SERVICE_INDICATOR_TYPES, +} = require('../EntityConstants'); const { JoiValidationConstants, } = require('../../../utilities/JoiValidationConstants'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -const { SERVICE_INDICATOR_TYPES } = require('../cases/CaseConstants'); -const ContactFactory = {}; - -ContactFactory.COUNTRY_TYPES = { - DOMESTIC: 'domestic', - INTERNATIONAL: 'international', -}; - -ContactFactory.US_STATES = { - AK: 'Alaska', - AL: 'Alabama', - AR: 'Arkansas', - AZ: 'Arizona', - CA: 'California', - CO: 'Colorado', - CT: 'Connecticut', - DC: 'District of Columbia', - DE: 'Delaware', - FL: 'Florida', - GA: 'Georgia', - HI: 'Hawaii', - IA: 'Iowa', - ID: 'Idaho', - IL: 'Illinois', - IN: 'Indiana', - KS: 'Kansas', - KY: 'Kentucky', - LA: 'Louisiana', - MA: 'Massachusetts', - MD: 'Maryland', - ME: 'Maine', - MI: 'Michigan', - MN: 'Minnesota', - MO: 'Missouri', - MS: 'Mississippi', - MT: 'Montana', - NC: 'North Carolina', - ND: 'North Dakota', - NE: 'Nebraska', - NH: 'New Hampshire', - NJ: 'New Jersey', - NM: 'New Mexico', - NV: 'Nevada', - NY: 'New York', - OH: 'Ohio', - OK: 'Oklahoma', - OR: 'Oregon', - PA: 'Pennsylvania', - RI: 'Rhode Island', - SC: 'South Carolina', - SD: 'South Dakota', - TN: 'Tennessee', - TX: 'Texas', - UT: 'Utah', - VA: 'Virginia', - VT: 'Vermont', - WA: 'Washington', - WI: 'Wisconsin', - WV: 'West Virginia', - WY: 'Wyoming', -}; - -ContactFactory.PARTY_TYPES = { - conservator: 'Conservator', - corporation: 'Corporation', - custodian: 'Custodian', - donor: 'Donor', - estate: 'Estate with an executor/personal representative/fiduciary/etc.', - estateWithoutExecutor: - 'Estate without an executor/personal representative/fiduciary/etc.', - guardian: 'Guardian', - nextFriendForIncompetentPerson: - 'Next friend for a legally incompetent person (without a guardian, conservator, or other like fiduciary)', - nextFriendForMinor: - 'Next friend for a minor (without a guardian, conservator, or other like fiduciary)', - partnershipAsTaxMattersPartner: 'Partnership (as the Tax Matters Partner)', - partnershipBBA: - 'Partnership (as a partnership representative under the BBA regime)', - partnershipOtherThanTaxMatters: - 'Partnership (as a partner other than Tax Matters Partner)', - petitioner: 'Petitioner', - petitionerDeceasedSpouse: 'Petitioner & deceased spouse', - petitionerSpouse: 'Petitioner & spouse', - survivingSpouse: 'Surviving spouse', - transferee: 'Transferee', - trust: 'Trust', -}; -ContactFactory.BUSINESS_TYPES = { - corporation: ContactFactory.PARTY_TYPES.corporation, - partnershipAsTaxMattersPartner: - ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, - partnershipBBA: ContactFactory.PARTY_TYPES.partnershipBBA, - partnershipOtherThanTaxMatters: - ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, -}; - -ContactFactory.ESTATE_TYPES = { - estate: ContactFactory.PARTY_TYPES.estate, - estateWithoutExecutor: ContactFactory.PARTY_TYPES.estateWithoutExecutor, - trust: ContactFactory.PARTY_TYPES.trust, -}; - -ContactFactory.OTHER_TYPES = { - conservator: ContactFactory.PARTY_TYPES.conservator, - custodian: ContactFactory.PARTY_TYPES.custodian, - guardian: ContactFactory.PARTY_TYPES.guardian, - nextFriendForIncompetentPerson: - ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, - nextFriendForMinor: ContactFactory.PARTY_TYPES.nextFriendForMinor, -}; +const ContactFactory = {}; ContactFactory.DOMESTIC_VALIDATION_ERROR_MESSAGES = { address1: 'Enter mailing address', @@ -163,10 +58,7 @@ const commonValidationRequirements = { .optional(), }; const domesticValidationObject = { - countryType: joi - .string() - .valid(ContactFactory.COUNTRY_TYPES.DOMESTIC) - .required(), + countryType: joi.string().valid(COUNTRY_TYPES.DOMESTIC).required(), ...commonValidationRequirements, state: joi.string().required(), postalCode: JoiValidationConstants.US_POSTAL_CODE.required(), @@ -174,10 +66,7 @@ const domesticValidationObject = { const internationalValidationObject = { country: joi.string().required(), - countryType: joi - .string() - .valid(ContactFactory.COUNTRY_TYPES.INTERNATIONAL) - .required(), + countryType: joi.string().valid(COUNTRY_TYPES.INTERNATIONAL).required(), ...commonValidationRequirements, postalCode: joi.string().required(), }; @@ -192,11 +81,11 @@ const internationalValidationObject = { * @returns {object} the joi validation object */ ContactFactory.getValidationObject = ({ - countryType = ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType = COUNTRY_TYPES.DOMESTIC, isPaper = false, }) => { const baseValidationObject = - countryType === ContactFactory.COUNTRY_TYPES.DOMESTIC + countryType === COUNTRY_TYPES.DOMESTIC ? domesticValidationObject : internationalValidationObject; @@ -214,9 +103,9 @@ ContactFactory.getValidationObject = ({ * @returns {object} the error message map object which maps errors to custom messages */ ContactFactory.getErrorToMessageMap = ({ - countryType = ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType = COUNTRY_TYPES.DOMESTIC, }) => { - return countryType === ContactFactory.COUNTRY_TYPES.DOMESTIC + return countryType === COUNTRY_TYPES.DOMESTIC ? ContactFactory.DOMESTIC_VALIDATION_ERROR_MESSAGES : ContactFactory.INTERNATIONAL_VALIDATION_ERROR_MESSAGES; }; @@ -278,87 +167,87 @@ const getContactConstructor = ({ const { getPetitionerTrustContact } = require('./PetitionerTrustContact'); const { getSurvivingSpouseContact } = require('./SurvivingSpouseContact'); return { - [ContactFactory.PARTY_TYPES.petitioner]: { + [PARTY_TYPES.petitioner]: { primary: getPetitionerPrimaryContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.transferee]: { + [PARTY_TYPES.transferee]: { primary: getPetitionerPrimaryContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.donor]: { + [PARTY_TYPES.donor]: { primary: getPetitionerPrimaryContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse]: { + [PARTY_TYPES.petitionerDeceasedSpouse]: { primary: getPetitionerPrimaryContact({ countryType, isPaper }), secondary: getPetitionerDeceasedSpouseContact({ countryType, isPaper }), }[contactType], - [ContactFactory.PARTY_TYPES.survivingSpouse]: { + [PARTY_TYPES.survivingSpouse]: { primary: getSurvivingSpouseContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.petitionerSpouse]: { + [PARTY_TYPES.petitionerSpouse]: { primary: getPetitionerPrimaryContact({ countryType, isPaper }), secondary: getPetitionerSpouseContact({ countryType, isPaper }), }[contactType], - [ContactFactory.PARTY_TYPES.corporation]: { + [PARTY_TYPES.corporation]: { primary: getPetitionerCorporationContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.estateWithoutExecutor]: { + [PARTY_TYPES.estateWithoutExecutor]: { primary: getPetitionerIntermediaryContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner]: { + [PARTY_TYPES.partnershipAsTaxMattersPartner]: { primary: getPartnershipAsTaxMattersPartnerPrimaryContact({ countryType, isPaper, }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters]: { + [PARTY_TYPES.partnershipOtherThanTaxMatters]: { primary: getPartnershipOtherThanTaxMattersPrimaryContact({ countryType, isPaper, }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.nextFriendForMinor]: { + [PARTY_TYPES.nextFriendForMinor]: { primary: getNextFriendForMinorContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson]: { + [PARTY_TYPES.nextFriendForIncompetentPerson]: { primary: getNextFriendForIncompetentPersonContact({ countryType, isPaper, }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.estate]: { + [PARTY_TYPES.estate]: { primary: getPetitionerEstateWithExecutorPrimaryContact({ countryType, isPaper, }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.partnershipBBA]: { + [PARTY_TYPES.partnershipBBA]: { primary: getPartnershipBBAPrimaryContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.trust]: { + [PARTY_TYPES.trust]: { primary: getPetitionerTrustContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.conservator]: { + [PARTY_TYPES.conservator]: { primary: getPetitionerConservatorContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.guardian]: { + [PARTY_TYPES.guardian]: { primary: getPetitionerGuardianContact({ countryType, isPaper }), secondary: null, }[contactType], - [ContactFactory.PARTY_TYPES.custodian]: { + [PARTY_TYPES.custodian]: { primary: getPetitionerCustodianContact({ countryType, isPaper }), secondary: null, }[contactType], diff --git a/shared/src/business/entities/contacts/ContactFactory.test.js b/shared/src/business/entities/contacts/ContactFactory.test.js index a6e8dda8fb6..93da960c05c 100644 --- a/shared/src/business/entities/contacts/ContactFactory.test.js +++ b/shared/src/business/entities/contacts/ContactFactory.test.js @@ -1,7 +1,9 @@ -const { Case } = require('../cases/Case'); +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); const { CaseExternal } = require('../cases/CaseExternal'); const { CaseInternal } = require('../cases/CaseInternal'); -const { ContactFactory } = require('./ContactFactory'); +const { PARTY_TYPES, PAYMENT_STATUS } = require('../EntityConstants'); let caseExternal; @@ -14,7 +16,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -45,7 +47,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -77,7 +79,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Fresno, California', @@ -108,7 +110,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -127,7 +129,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -159,7 +161,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -190,7 +192,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -210,7 +212,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -243,7 +245,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -263,7 +265,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -295,7 +297,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -315,7 +317,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.conservator, + partyType: PARTY_TYPES.conservator, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -345,7 +347,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.conservator, + partyType: PARTY_TYPES.conservator, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -365,7 +367,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -395,7 +397,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -415,7 +417,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.custodian, + partyType: PARTY_TYPES.custodian, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -445,7 +447,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.custodian, + partyType: PARTY_TYPES.custodian, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -465,7 +467,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.donor, + partyType: PARTY_TYPES.donor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -496,7 +498,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.donor, + partyType: PARTY_TYPES.donor, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -516,7 +518,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -547,7 +549,7 @@ describe('Petition', () => { hasIrsNotice: true, irsNoticeDate: '2009-10-13T08:06:07.539Z', mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, petitionFile: {}, petitionFileSize: 1, preferredTrialCity: 'Chattanooga, Tennessee', @@ -560,39 +562,42 @@ describe('Petition', () => { }); it('does not require phone number for internal cases', () => { - const caseInternal = new CaseInternal({ - caseCaption: 'Sisqo', - caseType: 'Other', - contactPrimary: { - address1: '876 12th Ave', - city: 'Nashville', - country: 'USA', - countryType: 'domestic', - email: 'someone@example.com', - name: 'Jimmy Dean', - postalCode: '05198', - state: 'AK', + const caseInternal = new CaseInternal( + { + caseCaption: 'Sisqo', + caseType: 'Other', + contactPrimary: { + address1: '876 12th Ave', + city: 'Nashville', + country: 'USA', + countryType: 'domestic', + email: 'someone@example.com', + name: 'Jimmy Dean', + postalCode: '05198', + state: 'AK', + }, + filingType: 'Myself', + hasIrsNotice: true, + irsNoticeDate: '2009-10-13T08:06:07.539Z', + mailingDate: 'testing', + partyType: PARTY_TYPES.transferee, + petitionFile: {}, + petitionFileSize: 1, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, + preferredTrialCity: 'Chattanooga, Tennessee', + procedureType: 'Small', + receivedAt: '2009-10-13T08:06:07.539Z', + requestForPlaceOfTrialFile: new File( + [], + 'requestForPlaceOfTrialFile.pdf', + ), + requestForPlaceOfTrialFileSize: 1, + signature: true, + stinFile: {}, + stinFileSize: 1, }, - filingType: 'Myself', - hasIrsNotice: true, - irsNoticeDate: '2009-10-13T08:06:07.539Z', - mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.transferee, - petitionFile: {}, - petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, - preferredTrialCity: 'Chattanooga, Tennessee', - procedureType: 'Small', - receivedAt: '2009-10-13T08:06:07.539Z', - requestForPlaceOfTrialFile: new File( - [], - 'requestForPlaceOfTrialFile.pdf', - ), - requestForPlaceOfTrialFileSize: 1, - signature: true, - stinFile: {}, - stinFileSize: 1, - }); + { applicationContext }, + ); expect(caseInternal.getFormattedValidationErrors()).toEqual(null); }); diff --git a/shared/src/business/entities/docketEntry/DocketEntryFactory.js b/shared/src/business/entities/docketEntry/DocketEntryFactory.js index cdc75de66b5..3668c43bdb2 100644 --- a/shared/src/business/entities/docketEntry/DocketEntryFactory.js +++ b/shared/src/business/entities/docketEntry/DocketEntryFactory.js @@ -1,18 +1,18 @@ const joi = require('@hapi/joi'); +const { + DOCUMENT_CATEGORY_MAP, + MAX_FILE_SIZE_BYTES, + MAX_FILE_SIZE_MB, +} = require('../EntityConstants'); const { ExternalDocumentFactory, } = require('../externalDocument/ExternalDocumentFactory'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -const { - MAX_FILE_SIZE_BYTES, - MAX_FILE_SIZE_MB, -} = require('../../../persistence/s3/getUploadPolicy'); const { VALIDATION_ERROR_MESSAGES, } = require('../externalDocument/ExternalDocumentInformationFactory'); -const { Document } = require('../Document'); const { getTimestampSchema } = require('../../../utilities/dateSchema'); const joiStrictTimestamp = getTimestampSchema(); @@ -126,7 +126,7 @@ function DocketEntryFactory(rawProps) { } const objectionDocumentTypes = [ - ...Document.CATEGORY_MAP['Motion'].map(entry => { + ...DOCUMENT_CATEGORY_MAP['Motion'].map(entry => { return entry.documentType; }), 'Motion to Withdraw Counsel (filed by petitioner)', diff --git a/shared/src/business/entities/externalDocument/ExternalDocumentInformationFactory.js b/shared/src/business/entities/externalDocument/ExternalDocumentInformationFactory.js index 160615602b7..49a6962385b 100644 --- a/shared/src/business/entities/externalDocument/ExternalDocumentInformationFactory.js +++ b/shared/src/business/entities/externalDocument/ExternalDocumentInformationFactory.js @@ -4,19 +4,19 @@ const { makeRequiredHelper, } = require('./externalDocumentHelpers'); const { - joiValidationDecorator, -} = require('../../../utilities/JoiValidationDecorator'); -const { + DOCUMENT_CATEGORY_MAP, MAX_FILE_SIZE_BYTES, MAX_FILE_SIZE_MB, -} = require('../../../persistence/s3/getUploadPolicy'); +} = require('../EntityConstants'); +const { + joiValidationDecorator, +} = require('../../../utilities/JoiValidationDecorator'); const { SecondaryDocumentInformationFactory, } = require('./SecondaryDocumentInformationFactory'); const { SupportingDocumentInformationFactory, } = require('./SupportingDocumentInformationFactory'); -const { Document } = require('../Document'); const { getTimestampSchema } = require('../../../utilities/dateSchema'); const { includes, isEqual, reduce, some, sortBy, values } = require('lodash'); @@ -221,7 +221,7 @@ ExternalDocumentInformationFactory.get = documentMetadata => { } const objectionDocumentTypes = [ - ...Document.CATEGORY_MAP['Motion'].map(entry => { + ...DOCUMENT_CATEGORY_MAP['Motion'].map(entry => { return entry.documentType; }), 'Motion to Withdraw Counsel (filed by petitioner)', diff --git a/shared/src/business/entities/externalDocument/SupportingDocumentInformationFactory.js b/shared/src/business/entities/externalDocument/SupportingDocumentInformationFactory.js index 9436c4644ba..ee1e8ef2859 100644 --- a/shared/src/business/entities/externalDocument/SupportingDocumentInformationFactory.js +++ b/shared/src/business/entities/externalDocument/SupportingDocumentInformationFactory.js @@ -2,12 +2,10 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -const { - MAX_FILE_SIZE_BYTES, -} = require('../../../persistence/s3/getUploadPolicy'); const { getTimestampSchema } = require('../../../utilities/dateSchema'); const { includes } = require('lodash'); const { makeRequiredHelper } = require('./externalDocumentHelpers'); +const { MAX_FILE_SIZE_BYTES } = require('../EntityConstants'); const joiStrictTimestamp = getTimestampSchema(); /** diff --git a/shared/src/business/entities/orders/Order.js b/shared/src/business/entities/orders/Order.js index 4f1c71a001c..8dfade0fea3 100644 --- a/shared/src/business/entities/orders/Order.js +++ b/shared/src/business/entities/orders/Order.js @@ -3,47 +3,6 @@ const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -Order.ORDER_TYPES = [ - { - documentType: 'Order', - eventCode: 'O', - }, - { - documentTitle: 'Order of Dismissal for Lack of Jurisdiction', - documentType: 'Order of Dismissal for Lack of Jurisdiction', - eventCode: 'ODJ', - }, - { - documentTitle: 'Order of Dismissal', - documentType: 'Order of Dismissal', - eventCode: 'OD', - }, - { - documentTitle: 'Order of Dismissal and Decision', - documentType: 'Order of Dismissal and Decision', - eventCode: 'ODD', - }, - { - documentTitle: 'Order to Show Cause', - documentType: 'Order to Show Cause', - eventCode: 'OSC', - }, - { - documentTitle: 'Order and Decision', - documentType: 'Order and Decision', - eventCode: 'OAD', - }, - { - documentTitle: 'Decision', - documentType: 'Decision', - eventCode: 'DEC', - }, - { - documentType: 'Notice', - eventCode: 'NOT', - }, -]; - /** * @param {object} rawOrder the raw order data * @constructor @@ -61,14 +20,16 @@ Order.VALIDATION_ERROR_MESSAGES = { orderBody: 'Order body is required.', }; +Order.VALIDATION_RULES = { + documentTitle: joi.string().max(100).required(), + documentType: joi.string().required(), // TODO: add enum + eventCode: joi.string().optional(), // TODO: add enum + orderBody: joi.string().max(500).required(), +}; + joiValidationDecorator( Order, - joi.object().keys({ - documentTitle: joi.string().required(), - documentType: joi.string().required(), - eventCode: joi.string().optional(), - orderBody: joi.string().required(), - }), + joi.object().keys(Order.VALIDATION_RULES), Order.VALIDATION_ERROR_MESSAGES, ); diff --git a/shared/src/business/entities/trialSessions/TrialSession.js b/shared/src/business/entities/trialSessions/TrialSession.js index da850ffbfe6..4eacc1b78af 100644 --- a/shared/src/business/entities/trialSessions/TrialSession.js +++ b/shared/src/business/entities/trialSessions/TrialSession.js @@ -8,125 +8,10 @@ const { const { createISODateString } = require('../../utilities/DateHandler'); const { getTimestampSchema } = require('../../../utilities/dateSchema'); const { isEmpty } = require('lodash'); +const { SESSION_TERMS, SESSION_TYPES } = require('../EntityConstants'); const joiStrictTimestamp = getTimestampSchema(); -const COMMON_CITIES = [ - { city: 'Birmingham', state: 'Alabama' }, - { city: 'Mobile', state: 'Alabama' }, - { city: 'Anchorage', state: 'Alaska' }, - { city: 'Phoenix', state: 'Arizona' }, - { city: 'Little Rock', state: 'Arkansas' }, - { city: 'Los Angeles', state: 'California' }, - { city: 'San Diego', state: 'California' }, - { city: 'San Francisco', state: 'California' }, - { city: 'Denver', state: 'Colorado' }, - { city: 'Hartford', state: 'Connecticut' }, - { city: 'Washington', state: 'District of Columbia' }, - { city: 'Jacksonville', state: 'Florida' }, - { city: 'Miami', state: 'Florida' }, - { city: 'Tampa', state: 'Florida' }, - { city: 'Atlanta', state: 'Georgia' }, - { city: 'Honolulu', state: 'Hawaii' }, - { city: 'Boise', state: 'Idaho' }, - { city: 'Chicago', state: 'Illinois' }, - { city: 'Indianapolis', state: 'Indiana' }, - { city: 'Des Moines', state: 'Iowa' }, - { city: 'Louisville', state: 'Kentucky' }, - { city: 'New Orleans', state: 'Louisiana' }, - { city: 'Baltimore', state: 'Maryland' }, - { city: 'Boston', state: 'Massachusetts' }, - { city: 'Detroit', state: 'Michigan' }, - { city: 'St. Paul', state: 'Minnesota' }, - { city: 'Jackson', state: 'Mississippi' }, - { city: 'Kansas City', state: 'Missouri' }, - { city: 'St. Louis', state: 'Missouri' }, - { city: 'Helena', state: 'Montana' }, - { city: 'Omaha', state: 'Nebraska' }, - { city: 'Las Vegas', state: 'Nevada' }, - { city: 'Reno', state: 'Nevada' }, - { city: 'Albuquerque', state: 'New Mexico' }, - { city: 'Buffalo', state: 'New York' }, - { city: 'New York City', state: 'New York' }, - { city: 'Winston-Salem', state: 'North Carolina' }, - { city: 'Cincinnati', state: 'Ohio' }, - { city: 'Cleveland', state: 'Ohio' }, - { city: 'Columbus', state: 'Ohio' }, - { city: 'Oklahoma City', state: 'Oklahoma' }, - { city: 'Portland', state: 'Oregon' }, - { city: 'Philadelphia', state: 'Pennsylvania' }, - { city: 'Pittsburgh', state: 'Pennsylvania' }, - { city: 'Columbia', state: 'South Carolina' }, - { city: 'Knoxville', state: 'Tennessee' }, - { city: 'Memphis', state: 'Tennessee' }, - { city: 'Nashville', state: 'Tennessee' }, - { city: 'Dallas', state: 'Texas' }, - { city: 'El Paso', state: 'Texas' }, - { city: 'Houston', state: 'Texas' }, - { city: 'Lubbock', state: 'Texas' }, - { city: 'San Antonio', state: 'Texas' }, - { city: 'Salt Lake City', state: 'Utah' }, - { city: 'Richmond', state: 'Virginia' }, - { city: 'Seattle', state: 'Washington' }, - { city: 'Spokane', state: 'Washington' }, - { city: 'Charleston', state: 'West Virginia' }, - { city: 'Milwaukee', state: 'Wisconsin' }, -]; - -const SMALL_CITIES = [ - { city: 'Fresno', state: 'California' }, - { city: 'Tallahassee', state: 'Florida' }, - { city: 'Pocatello', state: 'Idaho' }, - { city: 'Peoria', state: 'Illinois' }, - { city: 'Wichita', state: 'Kansas' }, - { city: 'Shreveport', state: 'Louisiana' }, - { city: 'Portland', state: 'Maine' }, - { city: 'Billings', state: 'Montana' }, - { city: 'Albany', state: 'New York' }, - { city: 'Syracuse', state: 'New York' }, - { city: 'Bismarck', state: 'North Dakota' }, - { city: 'Aberdeen', state: 'South Dakota' }, - { city: 'Burlington', state: 'Vermont' }, - { city: 'Roanoke', state: 'Virginia' }, - { city: 'Cheyenne', state: 'Wyoming' }, - ...COMMON_CITIES, -]; - -TrialSession.TRIAL_CITIES = { - ALL: SMALL_CITIES, - REGULAR: COMMON_CITIES, - SMALL: SMALL_CITIES, -}; - -TrialSession.TRIAL_CITY_STRINGS = SMALL_CITIES.map( - location => `${location.city}, ${location.state}`, -); - -TrialSession.SESSION_TERMS = ['Winter', 'Fall', 'Spring', 'Summer']; - -TrialSession.SESSION_TYPES = [ - 'Regular', - 'Small', - 'Hybrid', - 'Special', - 'Motion/Hearing', -]; - -TrialSession.SESSION_STATUS_GROUPS = { - all: 'All', - closed: 'Closed', - new: 'New', - open: 'Open', -}; - -TrialSession.PROPERTIES_REQUIRED_FOR_CALENDARING = [ - 'address1', - 'city', - 'state', - 'postalCode', - 'judge', -]; - TrialSession.validationName = 'TrialSession'; /** @@ -202,6 +87,14 @@ TrialSession.VALIDATION_ERROR_MESSAGES = { trialLocation: 'Select a trial session location', }; +TrialSession.PROPERTIES_REQUIRED_FOR_CALENDARING = [ + 'address1', + 'city', + 'state', + 'postalCode', + 'judge', +]; + TrialSession.validationRules = { COMMON: { address1: joi.string().allow('').optional(), @@ -220,7 +113,7 @@ TrialSession.validationRules = { postalCode: JoiValidationConstants.US_POSTAL_CODE.optional(), sessionType: joi .string() - .valid(...TrialSession.SESSION_TYPES) + .valid(...SESSION_TYPES) .required(), startDate: joiStrictTimestamp.required(), startTime: JoiValidationConstants.TWENTYFOUR_HOUR_MINUTES, @@ -238,7 +131,7 @@ TrialSession.validationRules = { }), term: joi .string() - .valid(...TrialSession.SESSION_TERMS) + .valid(...SESSION_TERMS) .required(), termYear: joi.string().required(), trialClerk: joi.object().optional(), diff --git a/shared/src/business/entities/trialSessions/TrialSessionWorkingCopy.js b/shared/src/business/entities/trialSessions/TrialSessionWorkingCopy.js index 411a46ba18c..0bbbedab938 100644 --- a/shared/src/business/entities/trialSessions/TrialSessionWorkingCopy.js +++ b/shared/src/business/entities/trialSessions/TrialSessionWorkingCopy.js @@ -2,18 +2,7 @@ const joi = require('@hapi/joi'); const { joiValidationDecorator, } = require('../../../utilities/JoiValidationDecorator'); -const { DOCKET_NUMBER_MATCHER } = require('../cases/CaseConstants'); - -TrialSessionWorkingCopy.TRIAL_STATUS_TYPES = [ - 'Set for Trial', - 'Dismissed', - 'Continued', - 'Rule 122', - 'A Basis Reached', - 'Settled', - 'Recall', - 'Taken Under Advisement', -]; +const { DOCKET_NUMBER_MATCHER } = require('../EntityConstants'); TrialSessionWorkingCopy.validationName = 'TrialSessionWorkingCopy'; diff --git a/shared/src/business/test/assignWorkItemsInteractor.e2e.test.js b/shared/src/business/test/assignWorkItemsInteractor.e2e.test.js index 54e70704828..7b15e9d8ca8 100644 --- a/shared/src/business/test/assignWorkItemsInteractor.e2e.test.js +++ b/shared/src/business/test/assignWorkItemsInteractor.e2e.test.js @@ -5,9 +5,9 @@ const { getDocumentQCInboxForUserInteractor, } = require('../useCases/workitems/getDocumentQCInboxForUserInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { createCaseInteractor } = require('../useCases/createCaseInteractor'); const { getCaseInteractor } = require('../useCases/getCaseInteractor'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('assignWorkItemsInteractor integration test', () => { @@ -48,7 +48,7 @@ describe('assignWorkItemsInteractor integration test', () => { ], filingType: 'Myself', hasIrsNotice: false, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Aberdeen, South Dakota', procedureType: 'Small', }, @@ -58,7 +58,7 @@ describe('assignWorkItemsInteractor integration test', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '3805d1ab-18d0-43ec-bafb-654e83405416', }), ); diff --git a/shared/src/business/test/completeWorkItemInteractor.e2e.test.js b/shared/src/business/test/completeWorkItemInteractor.e2e.test.js index 90d38962024..6425c305622 100644 --- a/shared/src/business/test/completeWorkItemInteractor.e2e.test.js +++ b/shared/src/business/test/completeWorkItemInteractor.e2e.test.js @@ -11,9 +11,9 @@ const { getSentMessagesForUserInteractor, } = require('../useCases/workitems/getSentMessagesForUserInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { createCaseInteractor } = require('../useCases/createCaseInteractor'); const { getCaseInteractor } = require('../useCases/getCaseInteractor'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('completeWorkItemInteractor integration test', () => { @@ -44,7 +44,7 @@ describe('completeWorkItemInteractor integration test', () => { contactSecondary: {}, filingType: 'Myself', hasIrsNotice: false, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Aberdeen, South Dakota', procedureType: 'Small', }, @@ -54,7 +54,7 @@ describe('completeWorkItemInteractor integration test', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'richard', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '3805d1ab-18d0-43ec-bafb-654e83405416', }), ); diff --git a/shared/src/business/test/createCaseFromPaperInteractor.e2e.test.js b/shared/src/business/test/createCaseFromPaperInteractor.e2e.test.js index 4fd5e61ddff..bbe192b83bd 100644 --- a/shared/src/business/test/createCaseFromPaperInteractor.e2e.test.js +++ b/shared/src/business/test/createCaseFromPaperInteractor.e2e.test.js @@ -1,3 +1,7 @@ +const { + CASE_STATUS_TYPES, + PAYMENT_STATUS, +} = require('../entities/EntityConstants'); const { createCaseFromPaperInteractor, } = require('../useCases/createCaseFromPaperInteractor'); @@ -8,10 +12,9 @@ const { getDocumentQCInboxForUserInteractor, } = require('../useCases/workitems/getDocumentQCInboxForUserInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); const { getCaseInteractor } = require('../useCases/getCaseInteractor'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('createCaseFromPaperInteractor integration test', () => { const RECEIVED_DATE = '2019-02-01T22:54:06.000Z'; @@ -23,7 +26,7 @@ describe('createCaseFromPaperInteractor integration test', () => { applicationContext.getCurrentUser.mockReturnValue({ name: 'Alex Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'a805d1ab-18d0-43ec-bafb-654e83405416', }); }); @@ -49,7 +52,7 @@ describe('createCaseFromPaperInteractor integration test', () => { mailingDate: 'testing', petitionFile: { name: 'something' }, petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, receivedAt: RECEIVED_DATE, requestForPlaceOfTrialFile: new File( [], @@ -90,7 +93,7 @@ describe('createCaseFromPaperInteractor integration test', () => { { assigneeId: 'a805d1ab-18d0-43ec-bafb-654e83405416', assigneeName: 'Alex Petitionsclerk', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, createdAt: RECEIVED_DATE, docketNumber: '101-19', docketNumberSuffix: null, @@ -133,7 +136,7 @@ describe('createCaseFromPaperInteractor integration test', () => { orderForRatification: false, orderToShowCause: false, receivedAt: RECEIVED_DATE, - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, userId: 'a805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -145,7 +148,7 @@ describe('createCaseFromPaperInteractor integration test', () => { expect(petitionsclerkInbox).toMatchObject([ { assigneeName: 'Alex Petitionsclerk', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19', document: { @@ -174,7 +177,7 @@ describe('createCaseFromPaperInteractor integration test', () => { expect(petitionsSectionInbox).toMatchObject([ { assigneeName: 'Alex Petitionsclerk', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19', document: { diff --git a/shared/src/business/test/createCaseInteractor.e2e.test.js b/shared/src/business/test/createCaseInteractor.e2e.test.js index 7375b23bb72..50b7f5b4cc6 100644 --- a/shared/src/business/test/createCaseInteractor.e2e.test.js +++ b/shared/src/business/test/createCaseInteractor.e2e.test.js @@ -2,10 +2,10 @@ const { getDocumentQCInboxForSectionInteractor, } = require('../useCases/workitems/getDocumentQCInboxForSectionInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { createCaseInteractor } = require('../useCases/createCaseInteractor'); const { getCaseInteractor } = require('../useCases/getCaseInteractor'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('createCase integration test', () => { @@ -36,7 +36,7 @@ describe('createCase integration test', () => { contactSecondary: {}, filingType: 'Myself', hasIrsNotice: false, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Aberdeen, South Dakota', procedureType: 'Small', }, @@ -71,7 +71,7 @@ describe('createCase integration test', () => { { assigneeId: null, assigneeName: null, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -88,7 +88,8 @@ describe('createCase integration test', () => { }, ], section: 'petitions', - sentBy: 'a805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Alex Petitionsclerk', + sentByUserId: 'a805d1ab-18d0-43ec-bafb-654e83405416', }, ], }, @@ -105,18 +106,18 @@ describe('createCase integration test', () => { noticeOfAttachments: false, orderForAmendedPetition: false, orderForAmendedPetitionAndFilingFee: false, - orderForFilingFee: false, + orderForFilingFee: true, orderForOds: false, orderForRatification: false, orderToShowCause: false, - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, userId: 'a805d1ab-18d0-43ec-bafb-654e83405416', }); applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'richard', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '3805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -129,7 +130,7 @@ describe('createCase integration test', () => { expect(docketsSectionInbox).toMatchObject([ { assigneeName: null, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -147,7 +148,8 @@ describe('createCase integration test', () => { }, ], section: 'petitions', - sentBy: 'a805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Alex Petitionsclerk', + sentByUserId: 'a805d1ab-18d0-43ec-bafb-654e83405416', }, ]); }); diff --git a/shared/src/business/test/createMockDocumentClient.js b/shared/src/business/test/createMockDocumentClient.js index dcc7dda5691..6846824a839 100644 --- a/shared/src/business/test/createMockDocumentClient.js +++ b/shared/src/business/test/createMockDocumentClient.js @@ -1,11 +1,11 @@ -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); const mockDynamoUsers = { ['user|1805d1ab-18d0-43ec-bafb-654e83405416 user|1805d1ab-18d0-43ec-bafb-654e83405416']: { email: 'docketclerk', name: 'Test Docketclerk', pk: 'user|1805d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, section: 'docket', sk: 'user|1805d1ab-18d0-43ec-bafb-654e83405416', userId: '1805d1ab-18d0-43ec-bafb-654e83405416', @@ -14,7 +14,7 @@ const mockDynamoUsers = { email: 'petitionsclerk', name: 'Test Petitionsclerk', pk: 'user|3805d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', sk: 'user|3805d1ab-18d0-43ec-bafb-654e83405416', userId: '3805d1ab-18d0-43ec-bafb-654e83405416', @@ -23,7 +23,7 @@ const mockDynamoUsers = { email: 'petitioner', name: 'Test Petitioner', pk: 'user|7805d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.petitioner, + role: ROLES.petitioner, sk: 'user|7805d1ab-18d0-43ec-bafb-654e83405416', userId: '7805d1ab-18d0-43ec-bafb-654e83405416', }, @@ -31,7 +31,7 @@ const mockDynamoUsers = { email: 'pettitionsclerk', name: 'Alex Petitionsclerk', pk: 'user|a805d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', sk: 'user|a805d1ab-18d0-43ec-bafb-654e83405416', userId: 'a805d1ab-18d0-43ec-bafb-654e83405416', diff --git a/shared/src/business/test/createTestApplicationContext.js b/shared/src/business/test/createTestApplicationContext.js index 037b27a6d15..9213d80f188 100644 --- a/shared/src/business/test/createTestApplicationContext.js +++ b/shared/src/business/test/createTestApplicationContext.js @@ -36,6 +36,9 @@ const { const { formatDocument, } = require('../../../src/business/utilities/getFormattedCaseDetail'); +const { + formatJudgeName, +} = require('../../../src/business/utilities/getFormattedJudgeName'); const { formattedTrialSessionDetails, } = require('../utilities/getFormattedTrialSessionDetails'); @@ -107,6 +110,7 @@ const { formatDollars } = require('../utilities/formatDollars'); const { getConstants } = require('../../../../web-client/src/getConstants'); const { getItem } = require('../../persistence/localStorage/getItem'); const { removeItem } = require('../../persistence/localStorage/removeItem'); +const { ROLES } = require('../entities/EntityConstants'); const { setItem } = require('../../persistence/localStorage/setItem'); const { updateCase } = require('../../persistence/dynamo/cases/updateCase'); const { User } = require('../entities/User'); @@ -115,12 +119,19 @@ const fakeData = 'JVBERi0xLjEKJcKlwrHDqwoKMSAwIG9iagogIDw8IC9UeXBlIC9DYXRhbG9nCiAgICAgL1BhZ2VzIDIgMCBSCiAgPj4KZW5kb2JqCgoyIDAgb2JqCiAgPDwgL1R5cGUgL1BhZ2VzCiAgICAgL0tpZHMgWzMgMCBSXQogICAgIC9Db3VudCAxCiAgICAgL01lZGlhQm94IFswIDAgMzAwIDE0NF0KICA+PgplbmRvYmoKCjMgMCBvYmoKICA8PCAgL1R5cGUgL1BhZ2UKICAgICAgL1BhcmVudCAyIDAgUgogICAgICAvUmVzb3VyY2VzCiAgICAgICA8PCAvRm9udAogICAgICAgICAgIDw8IC9GMQogICAgICAgICAgICAgICA8PCAvVHlwZSAvRm9udAogICAgICAgICAgICAgICAgICAvU3VidHlwZSAvVHlwZTEKICAgICAgICAgICAgICAgICAgL0Jhc2VGb250IC9UaW1lcy1Sb21hbgogICAgICAgICAgICAgICA+PgogICAgICAgICAgID4+CiAgICAgICA+PgogICAgICAvQ29udGVudHMgNCAwIFIKICA+PgplbmRvYmoKCjQgMCBvYmoKICA8PCAvTGVuZ3RoIDg0ID4+CnN0cmVhbQogIEJUCiAgICAvRjEgMTggVGYKICAgIDUgODAgVGQKICAgIChDb25ncmF0aW9ucywgeW91IGZvdW5kIHRoZSBFYXN0ZXIgRWdnLikgVGoKICBFVAplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTggMDAwMDAgbiAKMDAwMDAwMDA3NyAwMDAwMCBuIAowMDAwMDAwMTc4IDAwMDAwIG4gCjAwMDAwMDA0NTcgMDAwMDAgbiAKdHJhaWxlcgogIDw8ICAvUm9vdCAxIDAgUgogICAgICAvU2l6ZSA1CiAgPj4Kc3RhcnR4cmVmCjU2NQolJUVPRgo='; // TODO: Abstract for use elsewhere -const getFakeFile = () => { +const getFakeFile = returnArray => { const fakeFile = Buffer.from(fakeData, 'base64'); fakeFile.name = 'fakeFile.pdf'; + + if (returnArray) { + return new Uint8Array(fakeFile); + } + return fakeFile; }; +const getFakeFileUint8Array = () => getFakeFile(true); + const scannerResourcePath = path.join(__dirname, '../../../shared/test-assets'); const appContextProxy = (initial = {}, makeMock = true) => { @@ -173,6 +184,9 @@ const createTestApplicationContext = ({ user } = {}) => { createISODateStringFromObject: jest .fn() .mockImplementation(DateHandler.createISODateStringFromObject), + dateStringsCompared: jest + .fn() + .mockImplementation(DateHandler.dateStringsCompared), deconstructDate: jest.fn().mockImplementation(DateHandler.deconstructDate), filterEmptyStrings: jest.fn().mockImplementation(filterEmptyStrings), formatDateString: jest @@ -180,6 +194,7 @@ const createTestApplicationContext = ({ user } = {}) => { .mockImplementation(DateHandler.formatDateString), formatDocument: jest.fn().mockImplementation(formatDocument), formatDollars: jest.fn().mockImplementation(formatDollars), + formatJudgeName: jest.fn().mockImplementation(formatJudgeName), formatNow: jest.fn().mockImplementation(DateHandler.formatNow), formattedTrialSessionDetails: jest .fn() @@ -223,13 +238,20 @@ const createTestApplicationContext = ({ user } = {}) => { }); const getDocumentGeneratorsReturnMock = { + addressLabelCoverSheet: jest.fn().mockImplementation(getFakeFileUint8Array), caseInventoryReport: jest.fn().mockImplementation(getFakeFile), changeOfAddress: jest.fn().mockImplementation(getFakeFile), + coverSheet: jest.fn().mockImplementation(getFakeFile), docketRecord: jest.fn().mockImplementation(getFakeFile), noticeOfDocketChange: jest.fn().mockImplementation(getFakeFile), + noticeOfReceiptOfPetition: jest.fn().mockImplementation(getFakeFile), + order: jest.fn().mockImplementation(getFakeFile), pendingReport: jest.fn().mockImplementation(getFakeFile), receiptOfFiling: jest.fn().mockImplementation(getFakeFile), + standingPretrialNotice: jest.fn().mockImplementation(getFakeFile), standingPretrialOrder: jest.fn().mockImplementation(getFakeFile), + trialCalendar: jest.fn().mockImplementation(getFakeFile), + trialSessionPlanningReport: jest.fn().mockImplementation(getFakeFile), }; const getTemplateGeneratorsReturnMock = { @@ -239,9 +261,6 @@ const createTestApplicationContext = ({ user } = {}) => { generatePrintableDocketRecordTemplate: jest .fn() .mockResolvedValue('
'), - generateStandingPretrialNoticeTemplate: jest.fn(), - generateTrialCalendarTemplate: jest.fn(), - generateTrialSessionPlanningReportTemplate: jest.fn(), }; const mockGetChromiumBrowserReturnValue = { @@ -367,7 +386,7 @@ const createTestApplicationContext = ({ user } = {}) => { return new User( user || { name: 'richard', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'a805d1ab-18d0-43ec-bafb-654e83405416', }, ); @@ -394,7 +413,7 @@ const createTestApplicationContext = ({ user } = {}) => { getNotificationClient: jest.fn(), getNotificationGateway: appContextProxy(), getPdfJs: jest.fn().mockReturnValue(mockGetPdfJsReturnValue), - getPdfStyles: jest.fn(), + getPdfLib: jest.fn().mockReturnValue(require('pdf-lib')), getPersistenceGateway: mockGetPersistenceGateway, getPug: jest.fn(() => ({ compile: () => { diff --git a/shared/src/business/test/fileExternalDocumentInteractor.e2e.test.js b/shared/src/business/test/fileExternalDocumentInteractor.e2e.test.js index e4377c5e8f6..9ee8af2daf9 100644 --- a/shared/src/business/test/fileExternalDocumentInteractor.e2e.test.js +++ b/shared/src/business/test/fileExternalDocumentInteractor.e2e.test.js @@ -1,3 +1,7 @@ +const { + CASE_STATUS_TYPES, + PARTY_TYPES, +} = require('../entities/EntityConstants'); const { fileExternalDocumentInteractor, } = require('../useCases/externalDocument/fileExternalDocumentInteractor'); @@ -5,10 +9,9 @@ const { getDocumentQCInboxForSectionInteractor, } = require('../useCases/workitems/getDocumentQCInboxForSectionInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { createCaseInteractor } = require('../useCases/createCaseInteractor'); const { getCaseInteractor } = require('../useCases/getCaseInteractor'); +const { ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('fileExternalDocumentInteractor integration test', () => { @@ -19,7 +22,7 @@ describe('fileExternalDocumentInteractor integration test', () => { applicationContext.getCurrentUser.mockReturnValue({ name: 'Test Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '7805d1ab-18d0-43ec-bafb-654e83405416', }); }); @@ -55,7 +58,7 @@ describe('fileExternalDocumentInteractor integration test', () => { ], filingType: 'Myself', hasIrsNotice: false, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Aberdeen, South Dakota', procedureType: 'Small', }, @@ -169,7 +172,6 @@ describe('fileExternalDocumentInteractor integration test', () => { ], documents: [ { - caseId, documentId: '92eac064-9ca5-4c56-80a0-c5852c752277', documentType: 'Petition', filedBy: 'Petr. Test Petitioner', @@ -179,7 +181,7 @@ describe('fileExternalDocumentInteractor integration test', () => { assigneeId: null, assigneeName: null, caseId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -198,13 +200,12 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'petitions', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', updatedAt: '2019-03-01T22:54:06.000Z', }, ], }, { - caseId, documentId: '72de0fac-f63c-464f-ac71-0f54fd248484', documentType: 'Statement of Taxpayer Identification', filedBy: 'Petr. Test Petitioner', @@ -213,7 +214,6 @@ describe('fileExternalDocumentInteractor integration test', () => { }, { attachments: false, - caseId, certificateOfService: false, docketNumber: '201-19', documentId: '12de0fac-f63c-464f-ac71-0f54fd248484', @@ -230,12 +230,11 @@ describe('fileExternalDocumentInteractor integration test', () => { assigneeId: null, assigneeName: null, caseId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { attachments: false, - caseId, certificateOfService: false, certificateOfServiceDate: 'undefined-undefined-undefined', docketNumber: '201-19', @@ -258,12 +257,11 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', }, ], }, { - caseId, documentId: '22de0fac-f63c-464f-ac71-0f54fd248484', documentTitle: 'Brief in Support of Amended Answer', documentType: 'Brief in Support', @@ -279,7 +277,7 @@ describe('fileExternalDocumentInteractor integration test', () => { assigneeId: null, assigneeName: null, caseId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -303,7 +301,7 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', updatedAt: '2019-03-01T22:54:06.000Z', }, ], @@ -321,7 +319,7 @@ describe('fileExternalDocumentInteractor integration test', () => { { assigneeId: null, assigneeName: null, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -343,7 +341,7 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', updatedAt: '2019-03-01T22:54:06.000Z', }, ], @@ -364,7 +362,7 @@ describe('fileExternalDocumentInteractor integration test', () => { { assigneeId: null, assigneeName: null, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -389,7 +387,7 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', updatedAt: '2019-03-01T22:54:06.000Z', }, ], @@ -402,22 +400,22 @@ describe('fileExternalDocumentInteractor integration test', () => { noticeOfAttachments: false, orderForAmendedPetition: false, orderForAmendedPetitionAndFilingFee: false, - orderForFilingFee: false, + orderForFilingFee: true, orderForOds: false, orderForRatification: false, orderToShowCause: false, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Aberdeen, South Dakota', privatePractitioners: [], procedureType: 'Small', - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, userId: '7805d1ab-18d0-43ec-bafb-654e83405416', }); applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Docketclerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '1805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -433,12 +431,11 @@ describe('fileExternalDocumentInteractor integration test', () => { { assigneeId: null, assigneeName: null, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { attachments: false, - caseId, certificateOfService: false, docketNumber: '201-19', documentId: '12de0fac-f63c-464f-ac71-0f54fd248484', @@ -460,13 +457,12 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', }, { assigneeId: null, assigneeName: null, - caseId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -490,13 +486,13 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', updatedAt: '2019-03-01T22:54:06.000Z', }, { assigneeId: null, assigneeName: null, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -518,13 +514,13 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', updatedAt: '2019-03-01T22:54:06.000Z', }, { assigneeId: null, assigneeName: null, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-19', docketNumberWithSuffix: '101-19S', document: { @@ -549,7 +545,7 @@ describe('fileExternalDocumentInteractor integration test', () => { }, ], section: 'docket', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', }, ]); }); diff --git a/shared/src/business/test/forwardWorkItemInteractor.e2e.test.js b/shared/src/business/test/forwardWorkItemInteractor.e2e.test.js index a0f8336f58e..cde51a19bc1 100644 --- a/shared/src/business/test/forwardWorkItemInteractor.e2e.test.js +++ b/shared/src/business/test/forwardWorkItemInteractor.e2e.test.js @@ -8,9 +8,9 @@ const { getSentMessagesForUserInteractor, } = require('../useCases/workitems/getSentMessagesForUserInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { createCaseInteractor } = require('../useCases/createCaseInteractor'); const { getCaseInteractor } = require('../useCases/getCaseInteractor'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('forwardWorkItemInteractor integration test', () => { @@ -41,7 +41,7 @@ describe('forwardWorkItemInteractor integration test', () => { contactSecondary: {}, filingType: 'Myself', hasIrsNotice: false, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Aberdeen, South Dakota', procedureType: 'Small', }, @@ -51,7 +51,7 @@ describe('forwardWorkItemInteractor integration test', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'richard', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '3805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -116,7 +116,7 @@ describe('forwardWorkItemInteractor integration test', () => { applicationContext.getCurrentUser = () => { return new User({ name: 'bob', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '1805d1ab-18d0-43ec-bafb-654e83405416', }); }; diff --git a/shared/src/business/test/setWorkItemAsReadInteractor.e2e.test.js b/shared/src/business/test/setWorkItemAsReadInteractor.e2e.test.js index f003677a75b..898f8c4d0ad 100644 --- a/shared/src/business/test/setWorkItemAsReadInteractor.e2e.test.js +++ b/shared/src/business/test/setWorkItemAsReadInteractor.e2e.test.js @@ -8,9 +8,9 @@ const { setWorkItemAsReadInteractor, } = require('../useCases/workitems/setWorkItemAsReadInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { createCaseInteractor } = require('../useCases/createCaseInteractor'); const { getCaseInteractor } = require('../useCases/getCaseInteractor'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); const { WorkItem } = require('../entities/WorkItem'); @@ -42,7 +42,7 @@ describe('setWorkItemAsReadInteractor integration test', () => { contactSecondary: {}, filingType: 'Myself', hasIrsNotice: false, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Aberdeen, South Dakota', procedureType: 'Small', }, @@ -52,7 +52,7 @@ describe('setWorkItemAsReadInteractor integration test', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'richard', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '3805d1ab-18d0-43ec-bafb-654e83405416', }), ); diff --git a/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.js b/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.js index 1679e7ea8ba..12d5d984516 100644 --- a/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.js +++ b/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.js @@ -1,4 +1,4 @@ -const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); /** * updateCaseAutomaticBlock @@ -30,7 +30,7 @@ exports.updateCaseAutomaticBlock = async ({ caseId: caseEntity.caseId, }); } else if ( - caseEntity.status === Case.STATUS_TYPES.generalDocketReadyForTrial && + caseEntity.status === CASE_STATUS_TYPES.generalDocketReadyForTrial && !caseEntity.blocked ) { await applicationContext diff --git a/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.test.js b/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.test.js index f5d7a586471..e2bbd7867b6 100644 --- a/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.test.js +++ b/shared/src/business/useCaseHelper/automaticBlock/updateCaseAutomaticBlock.test.js @@ -1,6 +1,10 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { + AUTOMATIC_BLOCKED_REASONS, + CASE_STATUS_TYPES, +} = require('../../entities/EntityConstants'); const { MOCK_CASE, MOCK_CASE_WITHOUT_PENDING, @@ -31,7 +35,7 @@ describe('updateCaseAutomaticBlock', () => { expect(updatedCase).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pending, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pending, }); expect( applicationContext.getPersistenceGateway() @@ -55,7 +59,7 @@ describe('updateCaseAutomaticBlock', () => { expect(updatedCase).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.dueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.dueDate, }); expect( applicationContext.getPersistenceGateway() @@ -118,7 +122,7 @@ describe('updateCaseAutomaticBlock', () => { const caseEntity = new Case( { ...MOCK_CASE_WITHOUT_PENDING, - status: Case.STATUS_TYPES.generalDocketReadyForTrial, + status: CASE_STATUS_TYPES.generalDocketReadyForTrial, }, { applicationContext, diff --git a/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.js b/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.js index 7c39969db8b..0eff0d81b15 100644 --- a/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.js +++ b/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.js @@ -1,5 +1,6 @@ const { Case } = require('../../entities/cases/Case'); const { IrsPractitioner } = require('../../entities/IrsPractitioner'); +const { UserCase } = require('../../entities/UserCase'); /** * associateIrsPractitionerToCase @@ -26,12 +27,6 @@ exports.associateIrsPractitionerToCase = async ({ }); if (!isAssociated) { - await applicationContext.getPersistenceGateway().associateUserWithCase({ - applicationContext, - caseId: caseId, - userId: user.userId, - }); - const caseToUpdate = await applicationContext .getPersistenceGateway() .getCaseByCaseId({ @@ -39,6 +34,15 @@ exports.associateIrsPractitionerToCase = async ({ caseId, }); + const userCaseEntity = new UserCase(caseToUpdate); + + await applicationContext.getPersistenceGateway().associateUserWithCase({ + applicationContext, + caseId: caseId, + userCase: userCaseEntity.validate().toRawObject(), + userId: user.userId, + }); + const caseEntity = new Case(caseToUpdate, { applicationContext }); caseEntity.attachIrsPractitioner( diff --git a/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.test.js b/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.test.js index 94f12b5abf3..db7c6c2f68f 100644 --- a/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.test.js +++ b/shared/src/business/useCaseHelper/caseAssociation/associateIrsPractitionerToCase.test.js @@ -4,12 +4,10 @@ const { const { associateIrsPractitionerToCase, } = require('./associateIrsPractitionerToCase'); -const { - SERVICE_INDICATOR_TYPES, -} = require('../../entities/cases/CaseConstants'); const { MOCK_CASE } = require('../../../test/mockCase.js'); const { MOCK_USERS } = require('../../../test/mockUsers'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); +const { SERVICE_INDICATOR_TYPES } = require('../../entities/EntityConstants'); describe('associateIrsPractitionerToCase', () => { let caseRecord = { @@ -55,8 +53,8 @@ describe('associateIrsPractitionerToCase', () => { it('should not add mapping if already there', async () => { const user = { - name: 'Olivia Jade', - role: User.ROLES.irsPractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.irsPractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; @@ -85,8 +83,8 @@ describe('associateIrsPractitionerToCase', () => { .verifyCaseForUser.mockReturnValue(false); const user = { - name: 'Olivia Jade', - role: User.ROLES.irsPractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.irsPractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; diff --git a/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.js b/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.js index 4e452cd470f..c8f6acf86b1 100644 --- a/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.js +++ b/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.js @@ -1,8 +1,7 @@ -const { - SERVICE_INDICATOR_TYPES, -} = require('../../entities/cases/CaseConstants'); const { Case } = require('../../entities/cases/Case'); const { PrivatePractitioner } = require('../../entities/PrivatePractitioner'); +const { SERVICE_INDICATOR_TYPES } = require('../../entities/EntityConstants'); +const { UserCase } = require('../../entities/UserCase'); /** * associatePrivatePractitionerToCase @@ -35,12 +34,6 @@ exports.associatePrivatePractitionerToCase = async ({ }); if (!isAssociated) { - await applicationContext.getPersistenceGateway().associateUserWithCase({ - applicationContext, - caseId: caseId, - userId: user.userId, - }); - const caseToUpdate = await applicationContext .getPersistenceGateway() .getCaseByCaseId({ @@ -48,6 +41,15 @@ exports.associatePrivatePractitionerToCase = async ({ caseId, }); + const userCaseEntity = new UserCase(caseToUpdate); + + await applicationContext.getPersistenceGateway().associateUserWithCase({ + applicationContext, + caseId: caseId, + userCase: userCaseEntity.validate().toRawObject(), + userId: user.userId, + }); + const caseEntity = new Case(caseToUpdate, { applicationContext }); caseEntity.attachPrivatePractitioner( diff --git a/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.test.js b/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.test.js index 8ff7b1f9939..ef221f135bb 100644 --- a/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.test.js +++ b/shared/src/business/useCaseHelper/caseAssociation/associatePrivatePractitionerToCase.test.js @@ -5,17 +5,17 @@ const { associatePrivatePractitionerToCase, } = require('./associatePrivatePractitionerToCase'); const { + ROLES, SERVICE_INDICATOR_TYPES, -} = require('../../entities/cases/CaseConstants'); +} = require('../../entities/EntityConstants'); const { MOCK_USERS } = require('../../../test/mockUsers'); -const { User } = require('../../entities/User'); describe('associatePrivatePractitionerToCase', () => { let caseRecord; const practitionerUser = { - name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; @@ -62,7 +62,7 @@ describe('associatePrivatePractitionerToCase', () => { documentTitle: 'Petition', documentType: 'Petition', processingStatus: 'pending', - userId: 'petitioner', + userId: '8100e22a-c7f2-4574-b4f6-eb092fca9f35', }, ], filingType: 'Myself', diff --git a/shared/src/business/useCaseHelper/caseConfirmation/caseConfirmation.pug b/shared/src/business/useCaseHelper/caseConfirmation/caseConfirmation.pug deleted file mode 100644 index c6821122047..00000000000 --- a/shared/src/business/useCaseHelper/caseConfirmation/caseConfirmation.pug +++ /dev/null @@ -1,65 +0,0 @@ -doctype html -html(lang="en") - head - meta(charset="utf-8") - title NOTIFICATION OF RECEIPT OF PETITION, U.S. Tax Court Electronic Filing and Case Management System - style= css - body - .page-container - img.logo(src=logo) - h1.text-center United States Tax Court - p.text-center - | 400 Second Street, N.W. - br - | Washington D.C. 20217 - - p.text-center= todaysDate - h2.text-center.clear NOTIFICATION OF RECEIPT OF PETITION - - p.float-left.width-half= caseCaptionWithPostfix - p.float-right Docket Number: #{docketNumber} - - p.clear.margin-top-80 The Court received and filed your petition on #{receivedAtFormatted} and served it on respondent on #{servedDate}. - p.margin-bottom-20 - | (X) Request for Place of Trial at #{preferredTrialCity}. - - .info-box - h3 Your Docket Number: #{docketNumber} - p Please use this docket number on all papers and correspondence that you send to the Tax Court. Do not include your Social Security or Taxpayer Identification numbers on any documents you file with the Court. - - .info-box - h3 Internet Access: - p - | To obtain further information about proceeding in the Tax Court, please visit - b.link www.ustaxcourt.gov - | and select “Taxpayer Information”. - - .info-box - h3 Change of Address: - p - | You must notify the Clerk of the Court if you change your address. If you filed your petition in paper, see Tax Court Form 10, Notice of Change of Address, under “Forms” on the Tax Court’s Website at - b.link www.ustaxcourt.gov - |. If you filed your petition electronically, you may update your address under the “Case Information” tab in your case online. Failure to notify the Clerk of the Court of a change of your address can mean you do not receive notices and documents essential to your case and can lead to dismissal of your case. - - p.float-right.width-third - | Stephanie A. Servoss - br - | Clerk of the Court - - hr.clear.margin-top-80 - - .address - | #{name} - br - | #{address1} - br - if address2 - | #{address2} - br - if address3 - | #{address3} - br - | #{city}, #{state} #{postalCode} - if !address3 - br - | #{countryName} \ No newline at end of file diff --git a/shared/src/business/useCaseHelper/caseConfirmation/generateCaseConfirmationPdf.js b/shared/src/business/useCaseHelper/caseConfirmation/generateCaseConfirmationPdf.js deleted file mode 100644 index 53cdd7c4380..00000000000 --- a/shared/src/business/useCaseHelper/caseConfirmation/generateCaseConfirmationPdf.js +++ /dev/null @@ -1,130 +0,0 @@ -const DateHandler = require('../../utilities/DateHandler'); -const { - isAuthorized, - ROLE_PERMISSIONS, -} = require('../../../authorization/authorizationClientService'); -const { Case } = require('../../entities/cases/Case'); -const { UnauthorizedError } = require('../../../errors/errors'); - -/** - * - * @param {object} caseInfo a case entity - * @returns {object} the formatted information needed by the PDF - */ -const formattedCaseInfo = caseInfo => { - const { servedAt } = caseInfo.documents.find(doc => doc.servedAt); - const countryName = - caseInfo.contactPrimary.countryType !== 'domestic' - ? caseInfo.contactPrimary.country - : ''; - const formattedInfo = Object.assign( - { - caseCaptionWithPostfix: `${caseInfo.caseCaption} ${Case.CASE_CAPTION_POSTFIX}`, - countryName, - docketNumber: `${caseInfo.docketNumber}${ - caseInfo.docketNumberSuffix || '' - }`, - preferredTrialCity: caseInfo.preferredTrialCity, - receivedAtFormatted: DateHandler.formatDateString( - caseInfo.receivedAt, - 'MONTH_DAY_YEAR', - ), - servedDate: DateHandler.formatDateString(servedAt, 'MONTH_DAY_YEAR'), - todaysDate: DateHandler.formatNow('MONTH_DAY_YEAR'), - }, - caseInfo.contactPrimary, - ); - return formattedInfo; -}; - -/** - * - * @param {object} caseInfo a raw object representing a petition - * @returns {string} an html string resulting from rendering template with caseInfo - */ -const generateCaseConfirmationPage = async ({ - applicationContext, - caseEntity, -}) => { - const confirmSassContent = require('./../../assets/ustcPdf.scss_'); - const confirmTemplateContent = require('./caseConfirmation.pug_'); - const ustcLogoBufferBase64 = require('../../../../static/images/ustc_seal.png_'); - - const pug = applicationContext.getPug(); - const sass = applicationContext.getNodeSass(); - - const { css } = await new Promise(resolve => { - sass.render({ data: confirmSassContent }, (err, result) => { - return resolve(result); - }); - }); - const compiledFunction = pug.compile(confirmTemplateContent); - const html = compiledFunction({ - ...formattedCaseInfo(caseEntity), - css, - logo: ustcLogoBufferBase64, - }); - return html; -}; - -/** - * generateCaseConfirmationPdfInteractor - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {string} providers.caseEntity a case entity with its documents - * @returns {Promise<*>} the promise of the document having been uploaded - */ -exports.generateCaseConfirmationPdf = async ({ - applicationContext, - caseEntity, -}) => { - const user = applicationContext.getCurrentUser(); - - if (!isAuthorized(user, ROLE_PERMISSIONS.UPLOAD_DOCUMENT)) { - throw new UnauthorizedError('Unauthorized'); - } - - let browser = null; - let result = null; - - try { - browser = await applicationContext.getChromiumBrowser(); - let page = await browser.newPage(); - - const contentResult = await generateCaseConfirmationPage({ - applicationContext, - caseEntity, - }); - await page.setContent(contentResult); - - result = await page.pdf({ - displayHeaderFooter: false, - format: 'letter', - }); - } catch (error) { - applicationContext.logger.error(error); - throw error; - } finally { - if (browser !== null) { - await browser.close(); - } - } - - const caseConfirmationPdfName = caseEntity.getCaseConfirmationGeneratedPdfFileName(); - - await new Promise(resolve => { - const documentsBucket = applicationContext.getDocumentsBucketName(); - const s3Client = applicationContext.getStorageClient(); - - const params = { - Body: result, - Bucket: documentsBucket, - ContentType: 'application/pdf', - Key: caseConfirmationPdfName, - }; - - s3Client.upload(params, resolve); - }); - return result; -}; diff --git a/shared/src/business/useCaseHelper/caseConfirmation/generateCaseConfirmationPdf.test.js b/shared/src/business/useCaseHelper/caseConfirmation/generateCaseConfirmationPdf.test.js deleted file mode 100644 index 9d6c3360a46..00000000000 --- a/shared/src/business/useCaseHelper/caseConfirmation/generateCaseConfirmationPdf.test.js +++ /dev/null @@ -1,140 +0,0 @@ -const { - generateCaseConfirmationPdf, -} = require('./generateCaseConfirmationPdf'); -jest.mock('../../../authorization/authorizationClientService'); -const { - applicationContext, -} = require('../../test/createTestApplicationContext'); -const { - isAuthorized, -} = require('../../../authorization/authorizationClientService'); -const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); -const PDF_MOCK_BUFFER = 'Hello World'; - -const pageMock = { - addStyleTag: () => {}, - pdf: () => { - return PDF_MOCK_BUFFER; - }, - setContent: () => {}, -}; - -const chromiumBrowserMock = { - close: jest.fn(), - newPage: () => pageMock, -}; - -const mockCurrentUser = { - role: User.ROLES.petitioner, - userId: 'petitioner', -}; - -const s3Upload = jest.fn().mockImplementation((params, resolve) => resolve()); - -applicationContext.getCurrentUser.mockReturnValue(mockCurrentUser); -applicationContext.getDocumentsBucketName.mockReturnValue('DocumentBucketName'); -applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ - docketNumber: '101-19', -}); -applicationContext.getPug.mockReturnValue({ - compile: () => () => '', -}); -applicationContext.getStorageClient.mockReturnValue({ - upload: s3Upload, -}); - -describe('generateCaseConfirmationPdf', () => { - beforeEach(() => { - isAuthorized.mockReturnValue(true); - }); - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('fails to get chromium browser', async () => { - jest - .spyOn(applicationContext, 'getChromiumBrowser') - .mockImplementation(() => { - return null; - }); - - let error; - try { - await generateCaseConfirmationPdf({ - applicationContext, - caseEntity: { - ...MOCK_CASE, - documents: [{ servedAt: '2009-09-17T08:06:07.530Z' }], - }, - }); - } catch (err) { - error = err; - } - - expect(error).toBeDefined(); - expect(chromiumBrowserMock.close).not.toHaveBeenCalled(); - }); - - it('requires permissions', async () => { - isAuthorized.mockReturnValue(false); - let result, error; - try { - result = await generateCaseConfirmationPdf({ - applicationContext, - caseEntity: { - ...MOCK_CASE, - documents: [{ servedAt: '2009-09-17T08:06:07.530Z' }], - }, - }); - } catch (err) { - error = err; - } - expect(result).not.toBeDefined(); - expect(error.message).toEqual('Unauthorized'); - }); - - it('handles exceptions gracefully', async () => { - jest.spyOn(chromiumBrowserMock, 'newPage').mockImplementation(() => { - throw new Error('page problem'); - }); - applicationContext.getChromiumBrowser.mockReturnValue(chromiumBrowserMock); - let error; - try { - await generateCaseConfirmationPdf({ - applicationContext, - caseEntity: { - ...MOCK_CASE, - contactPrimary: { - countryType: 'domestic', - }, - documents: [{ servedAt: '2009-09-17T08:06:07.530Z' }], - }, - }); - } catch (err) { - error = err; - } - expect(error).toBeDefined(); - expect(applicationContext.logger.error).toHaveBeenCalled(); - expect(chromiumBrowserMock.close).toHaveBeenCalled(); - }); - - it('returns the pdf buffer produced by chromium', async () => { - await generateCaseConfirmationPdf({ - applicationContext, - caseEntity: { - ...MOCK_CASE, - contactPrimary: { - country: 'Canada', - countryType: 'international', - }, - documents: [{ servedAt: '2009-09-17T08:06:07.530Z' }], - getCaseConfirmationGeneratedPdfFileName() { - return ''; - }, - }, - }); - - expect(applicationContext.getStorageClient).toHaveBeenCalled(); - }); -}); diff --git a/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.js b/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.js index 7173dc6d579..545a1900b64 100644 --- a/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.js +++ b/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.js @@ -61,5 +61,6 @@ exports.generateCaseInventoryReportPdf = async ({ return await applicationContext.getUseCaseHelpers().saveFileAndGenerateUrl({ applicationContext, file: caseInventoryReportPdf, + useTempBucket: true, }); }; diff --git a/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.test.js b/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.test.js index 42bd3c0cf32..ab18605e98d 100644 --- a/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.test.js +++ b/shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf.test.js @@ -4,7 +4,7 @@ const { const { generateCaseInventoryReportPdf, } = require('./generateCaseInventoryReportPdf'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const mockCases = [ { @@ -23,7 +23,7 @@ describe('generateCaseInventoryReportPdf', () => { let user; beforeEach(() => { - user = { role: User.ROLES.petitionsClerk, userId: 'petitionsClerk' }; + user = { role: ROLES.petitionsClerk, userId: 'petitionsClerk' }; applicationContext.getCurrentUser.mockReturnValue(user); @@ -35,7 +35,7 @@ describe('generateCaseInventoryReportPdf', () => { }); it('throws an error if the user is unauthorized', async () => { - user = { role: User.ROLES.petitioner, userId: 'petitioner' }; + user = { role: ROLES.petitioner, userId: 'petitioner' }; applicationContext.getCurrentUser.mockReturnValue(user); await expect( diff --git a/shared/src/business/useCaseHelper/consolidatedCases/formatAndSortConsolidatedCases.js b/shared/src/business/useCaseHelper/consolidatedCases/formatAndSortConsolidatedCases.js new file mode 100644 index 00000000000..aa84405b6b3 --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/formatAndSortConsolidatedCases.js @@ -0,0 +1,28 @@ +const { Case } = require('../../entities/cases/Case'); + +/** + * Formats and sorts consolidated cases + * + * @param {object} arguments.consolidatedCases list of consolidated cases + * @param {object} arguments.leadCaseId the leadCaseId + * @param {object} arguments.userAssociatedCaseIdsMap the list of caseIds the user is associated with + * @returns {object} consolidated cases sorted by docket number + */ +exports.formatAndSortConsolidatedCases = ({ + consolidatedCases, + leadCaseId, + userAssociatedCaseIdsMap, +}) => { + const caseConsolidatedCases = []; + consolidatedCases.forEach(consolidatedCase => { + consolidatedCase.isRequestingUserAssociated = !!userAssociatedCaseIdsMap[ + consolidatedCase.caseId + ]; + + if (consolidatedCase.caseId !== leadCaseId) { + caseConsolidatedCases.push(consolidatedCase); + } + }); + + return Case.sortByDocketNumber(caseConsolidatedCases); +}; diff --git a/shared/src/business/useCaseHelper/consolidatedCases/formatAndSortConsolidatedCases.test.js b/shared/src/business/useCaseHelper/consolidatedCases/formatAndSortConsolidatedCases.test.js new file mode 100644 index 00000000000..d9a7fc911ef --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/formatAndSortConsolidatedCases.test.js @@ -0,0 +1,54 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + formatAndSortConsolidatedCases, +} = require('./formatAndSortConsolidatedCases'); + +describe('formatAndSortConsolidatedCases', () => { + it('should set isRequestingUserAssociated for each case associated with the lead caseId', async () => { + const result = await formatAndSortConsolidatedCases({ + applicationContext, + consolidatedCases: [{ caseId: '123' }], + leadCaseId: '456', + userAssociatedCaseIdsMap: { + '123': true, + }, + }); + + expect(result[0].isRequestingUserAssociated).toBe(true); + }); + + it("should add each case to the consolidatedCases list for the lead caseId when it's not the lead case", async () => { + const result = await formatAndSortConsolidatedCases({ + applicationContext, + consolidatedCases: [{ caseId: '123' }, { caseId: '456' }], + leadCaseId: '456', + userAssociatedCaseIdsMap: { + '123': true, + }, + }); + + expect(result.length).toBe(1); + expect(result[0].caseId).toBe('123'); + }); + + it('should return the list of consolidatedCases sorted by docketNumber', async () => { + const result = await formatAndSortConsolidatedCases({ + applicationContext, + consolidatedCases: [ + { caseId: '123', docketNumber: '999-20' }, + { caseId: '321', docketNumber: '123-20' }, + ], + leadCaseId: '456', + userAssociatedCaseIdsMap: { + '123': true, + '321': true, + }, + }); + + expect(result.length).toBe(2); + expect(result[0].docketNumber).toBe('123-20'); + expect(result[1].docketNumber).toBe('999-20'); + }); +}); diff --git a/shared/src/business/useCaseHelper/consolidatedCases/getConsolidatedCasesForLeadCase.js b/shared/src/business/useCaseHelper/consolidatedCases/getConsolidatedCasesForLeadCase.js new file mode 100644 index 00000000000..06c96c3fb68 --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/getConsolidatedCasesForLeadCase.js @@ -0,0 +1,28 @@ +const { Case } = require('../../entities/cases/Case'); + +/** + * Retrieves all cases associated with the provided leadCaseId. + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {object} providers.leadCaseId the leadCaseId + * @returns {object} a list of all cases associated with the leadCaseId + */ +exports.getConsolidatedCasesForLeadCase = async ({ + applicationContext, + leadCaseId, +}) => { + let consolidatedCases = await applicationContext + .getPersistenceGateway() + .getCasesByLeadCaseId({ + applicationContext, + leadCaseId, + }); + + consolidatedCases = Case.validateRawCollection(consolidatedCases, { + applicationContext, + filtered: true, + }); + + return consolidatedCases; +}; diff --git a/shared/src/business/useCaseHelper/consolidatedCases/getConsolidatedCasesForLeadCase.test.js b/shared/src/business/useCaseHelper/consolidatedCases/getConsolidatedCasesForLeadCase.test.js new file mode 100644 index 00000000000..b8d2ce7bd94 --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/getConsolidatedCasesForLeadCase.test.js @@ -0,0 +1,50 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + getConsolidatedCasesForLeadCase, +} = require('./getConsolidatedCasesForLeadCase'); +const { MOCK_CASE } = require('../../../test/mockCase'); + +describe('getConsolidatedCasesForLeadCase', () => { + it('should retrieve all cases associated with the leadCaseId', async () => { + applicationContext + .getPersistenceGateway() + .getCasesByLeadCaseId.mockReturnValue([MOCK_CASE]); + + await getConsolidatedCasesForLeadCase({ + applicationContext, + casesAssociatedWithUserOrLeadCaseMap: { + '123': MOCK_CASE, + }, + leadCaseId: '123', + userAssociatedCaseIdsMap: {}, + }); + + expect( + applicationContext.getPersistenceGateway().getCasesByLeadCaseId.mock + .calls[0][0], + ).toMatchObject({ leadCaseId: '123' }); + }); + + // TODO - Refactor Case constants into their own file. + // Test currently fails when trying to mock out Case.validateRawCollection + // due to circular dependency issue, UserCase pulls in Case validation text + // it('should validate the retrieved cases', async () => { + // const mockCaseId = '123'; + // applicationContext + // .getPersistenceGateway() + // .getCasesByLeadCaseId.mockResolvedValue([MOCK_CASE]); + + // getConsolidatedCasesForLeadCase({ + // applicationContext, + // casesAssociatedWithUserOrLeadCaseMap: { + // '123': MOCK_CASE, + // }, + // leadCaseId: mockCaseId, + // userAssociatedCaseIdsMap: {}, + // }); + + // expect(Case.validateRawCollection).toBeCalled(); + // }); +}); diff --git a/shared/src/business/useCaseHelper/consolidatedCases/getUnassociatedLeadCase.js b/shared/src/business/useCaseHelper/consolidatedCases/getUnassociatedLeadCase.js new file mode 100644 index 00000000000..0351886d97a --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/getUnassociatedLeadCase.js @@ -0,0 +1,15 @@ +/** + * Finds a lead case when it is not associated with the current user + * + * @param {object} arguments.consolidatedCases the list of consolidated cases + * @param {object} arguments.leadCaseId the leadCaseId + * @returns {object} the lead case + */ +exports.getUnassociatedLeadCase = ({ consolidatedCases, leadCaseId }) => { + const leadCase = consolidatedCases.find( + consolidatedCase => consolidatedCase.caseId === leadCaseId, + ); + leadCase.isRequestingUserAssociated = false; + + return leadCase; +}; diff --git a/shared/src/business/useCaseHelper/consolidatedCases/getUnassociatedLeadCase.test.js b/shared/src/business/useCaseHelper/consolidatedCases/getUnassociatedLeadCase.test.js new file mode 100644 index 00000000000..bfc87c59e57 --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/getUnassociatedLeadCase.test.js @@ -0,0 +1,25 @@ +const { getUnassociatedLeadCase } = require('./getUnassociatedLeadCase'); +const { MOCK_CASE } = require('../../../test/mockCase'); + +describe('getUnassociatedLeadCase', () => { + it('should set the found case isRequestingUserAssociated to false', () => { + const result = getUnassociatedLeadCase({ + consolidatedCases: [MOCK_CASE], + leadCaseId: MOCK_CASE.caseId, + }); + + expect(result.isRequestingUserAssociated).toBe(false); + }); + + it('should return the found lead case', () => { + let casesAssociatedWithUserOrLeadCaseMap = {}; + + const result = getUnassociatedLeadCase({ + casesAssociatedWithUserOrLeadCaseMap, + consolidatedCases: [MOCK_CASE], + leadCaseId: MOCK_CASE.caseId, + }); + + expect(result).toMatchObject(MOCK_CASE); + }); +}); diff --git a/shared/src/business/useCaseHelper/consolidatedCases/processUserAssociatedCases.js b/shared/src/business/useCaseHelper/consolidatedCases/processUserAssociatedCases.js new file mode 100644 index 00000000000..526fb10bfdd --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/processUserAssociatedCases.js @@ -0,0 +1,36 @@ +/** + * Given a list of cases associated with the current user, creates a map of + * consolidated and lead cases. + * + * @param {object} openUserCases the list of open cases associated with the current user + * @returns {object} casesAssociatedWithUserOrLeadCaseMap - a map of + * consolidated and lead cases. leadCaseIdsAssociatedWithUser - a list of leadCaseIds + * associated with the current user. userAssociatedCaseIdsMap - a map of open cases associated + * with the current user + */ +exports.processUserAssociatedCases = openUserCases => { + let casesAssociatedWithUserOrLeadCaseMap = {}; + let userAssociatedCaseIdsMap = {}; + let leadCaseIdsAssociatedWithUser = []; + + openUserCases.forEach(caseRecord => { + const { caseId, leadCaseId } = caseRecord; + const caseIsALeadCase = leadCaseId === caseId; + + caseRecord.isRequestingUserAssociated = true; + userAssociatedCaseIdsMap[caseId] = true; + + if (!leadCaseId || caseIsALeadCase) { + casesAssociatedWithUserOrLeadCaseMap[caseId] = caseRecord; + } + if (leadCaseId && !leadCaseIdsAssociatedWithUser.includes(leadCaseId)) { + leadCaseIdsAssociatedWithUser.push(leadCaseId); + } + }); + + return { + casesAssociatedWithUserOrLeadCaseMap, + leadCaseIdsAssociatedWithUser, + userAssociatedCaseIdsMap, + }; +}; diff --git a/shared/src/business/useCaseHelper/consolidatedCases/processUserAssociatedCases.test.js b/shared/src/business/useCaseHelper/consolidatedCases/processUserAssociatedCases.test.js new file mode 100644 index 00000000000..614f452c5db --- /dev/null +++ b/shared/src/business/useCaseHelper/consolidatedCases/processUserAssociatedCases.test.js @@ -0,0 +1,74 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { MOCK_CASE } = require('../../../test/mockCase'); +const { processUserAssociatedCases } = require('./processUserAssociatedCases'); + +describe('processUserAssociatedCases', () => { + let mockFoundCasesList; + + beforeEach(() => { + mockFoundCasesList = [MOCK_CASE]; + }); + + it('should set isRequestingUserAssociated to true for each case', async () => { + processUserAssociatedCases(mockFoundCasesList); + + expect(MOCK_CASE.isRequestingUserAssociated).toBe(true); + }); + + it('should add a case to casesAssociatedWithUserOrLeadCaseMap when it is a lead case', async () => { + mockFoundCasesList = [{ ...MOCK_CASE, isLeadCase: true }]; + + const result = processUserAssociatedCases(mockFoundCasesList); + + expect( + result.casesAssociatedWithUserOrLeadCaseMap[MOCK_CASE.caseId], + ).toEqual({ ...MOCK_CASE, isLeadCase: true }); + }); + + it('should add a case to casesAssociatedWithUserOrLeadCaseMap when it does not have a leadCaseId', async () => { + const result = processUserAssociatedCases(mockFoundCasesList); + + expect( + result.casesAssociatedWithUserOrLeadCaseMap[MOCK_CASE.caseId], + ).toEqual(MOCK_CASE); + }); + + it("should add a case's caseId to userAssociatedCaseIdsMap", async () => { + const result = processUserAssociatedCases(mockFoundCasesList); + + expect(result.userAssociatedCaseIdsMap[MOCK_CASE.caseId]).toEqual(true); + }); + + it('should add a case to leadCaseIdsAssociatedWithUser if it has a leadCaseId and is not associated with the user', async () => { + let mockCaseWithLeadCaseId = { + ...MOCK_CASE, + leadCaseId: applicationContext.getUniqueId(), + }; + mockFoundCasesList = [mockCaseWithLeadCaseId]; + + const result = processUserAssociatedCases(mockFoundCasesList); + + expect( + result.leadCaseIdsAssociatedWithUser.includes( + mockCaseWithLeadCaseId.leadCaseId.toString(), + ), + ).toBe(true); + }); + + it('should populate casesAssociatedWithUserOrLeadCaseMap, leadCaseIdsAssociatedWithUser and userAssociatedCaseIdsMap', async () => { + let mockCaseWithLeadCaseId = { + ...MOCK_CASE, + isLeadCase: true, + leadCaseId: applicationContext.getUniqueId(), + }; + mockFoundCasesList = [mockCaseWithLeadCaseId, MOCK_CASE]; + + const result = processUserAssociatedCases(mockFoundCasesList); + + expect(result.casesAssociatedWithUserOrLeadCaseMap).not.toBe({}); + expect(result.leadCaseIdsAssociatedWithUser.length).toBe(1); + expect(result.userAssociatedCaseIdsMap).not.toBe({}); + }); +}); diff --git a/shared/src/business/useCaseHelper/countPagesInDocument.js b/shared/src/business/useCaseHelper/countPagesInDocument.js index 58db3cba61f..dd48c24160d 100644 --- a/shared/src/business/useCaseHelper/countPagesInDocument.js +++ b/shared/src/business/useCaseHelper/countPagesInDocument.js @@ -1,6 +1,5 @@ -const { PDFDocument } = require('pdf-lib'); - exports.countPagesInDocument = async ({ applicationContext, documentId }) => { + const { PDFDocument } = await applicationContext.getPdfLib(); const bytes = await applicationContext.getPersistenceGateway().getDocument({ applicationContext, documentId, diff --git a/shared/src/business/useCaseHelper/courtIssuedDocument/generatePaperServiceAddressPagePdf.js b/shared/src/business/useCaseHelper/courtIssuedDocument/generatePaperServiceAddressPagePdf.js deleted file mode 100644 index 3817d3d3065..00000000000 --- a/shared/src/business/useCaseHelper/courtIssuedDocument/generatePaperServiceAddressPagePdf.js +++ /dev/null @@ -1,87 +0,0 @@ -const { - isAuthorized, - ROLE_PERMISSIONS, -} = require('../../../authorization/authorizationClientService'); -const { UnauthorizedError } = require('../../../errors/errors'); - -/** - * generatePaperServiceAddressPage - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {object} providers.contactData the contact data - * @returns {string} an html string resulting from rendering template with contactData - */ -const generatePaperServiceAddressPage = async ({ - applicationContext, - contactData, - docketNumberWithSuffix, -}) => { - const baseSassContent = require('../../assets/ustcPdf.scss_'); - const paperServiceAddressPageTemplate = require('./paperServiceAddressPage.pug_'); - - const pug = applicationContext.getPug(); - const sass = applicationContext.getNodeSass(); - - const { css } = await new Promise(resolve => { - sass.render({ data: baseSassContent }, (err, result) => { - return resolve(result); - }); - }); - const compiledFunction = pug.compile(paperServiceAddressPageTemplate); - const html = compiledFunction({ - ...contactData, - css, - docketNumberWithSuffix, - }); - return html; -}; - -/** - * generatePaperServiceAddressPagePdf - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {object} providers.contactData the contact data - * @returns {Uint8Array} the generated pdf data - */ -exports.generatePaperServiceAddressPagePdf = async ({ - applicationContext, - contactData, - docketNumberWithSuffix, -}) => { - const user = applicationContext.getCurrentUser(); - - if (!isAuthorized(user, ROLE_PERMISSIONS.UPLOAD_DOCUMENT)) { - throw new UnauthorizedError('Unauthorized'); - } - - let browser = null; - let result = null; - - try { - browser = await applicationContext.getChromiumBrowser(); - let page = await browser.newPage(); - - const contentResult = await generatePaperServiceAddressPage({ - applicationContext, - contactData, - docketNumberWithSuffix, - }); - await page.setContent(contentResult); - - result = await page.pdf({ - displayHeaderFooter: false, - format: 'letter', - }); - } catch (error) { - applicationContext.logger.error(error); - throw error; - } finally { - if (browser !== null) { - await browser.close(); - } - } - - return result; -}; diff --git a/shared/src/business/useCaseHelper/courtIssuedDocument/generatePaperServiceAddressPagePdf.test.js b/shared/src/business/useCaseHelper/courtIssuedDocument/generatePaperServiceAddressPagePdf.test.js deleted file mode 100644 index 8a5584fba0c..00000000000 --- a/shared/src/business/useCaseHelper/courtIssuedDocument/generatePaperServiceAddressPagePdf.test.js +++ /dev/null @@ -1,126 +0,0 @@ -const { - applicationContext, -} = require('../../test/createTestApplicationContext'); -const { - generatePaperServiceAddressPagePdf, -} = require('./generatePaperServiceAddressPagePdf'); -jest.mock('../../../authorization/authorizationClientService'); -const { - isAuthorized, -} = require('../../../authorization/authorizationClientService'); -const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); -const PDF_MOCK_BUFFER = 'Hello World'; - -const pageMock = { - addStyleTag: () => {}, - pdf: () => { - return PDF_MOCK_BUFFER; - }, - setContent: () => {}, -}; - -const chromiumBrowserMock = { - close: jest.fn(), - newPage: () => pageMock, -}; - -const mockCurrentUser = { - role: User.ROLES.petitioner, - userId: 'petitioner', -}; - -const mockCase = { docketNumber: '101-19' }; - -describe('generatePaperServiceAddressPagePdf', () => { - beforeEach(() => { - applicationContext.getChromiumBrowser.mockReturnValue(chromiumBrowserMock); - applicationContext.getCurrentUser.mockReturnValue(mockCurrentUser); - applicationContext - .getPersistenceGateway() - .getCaseByCaseId.mockReturnValue(mockCase); - applicationContext.getPug.mockReturnValue({ compile: () => () => '' }); - isAuthorized.mockReturnValue(true); - }); - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('fails to get chromium browser', async () => { - jest - .spyOn(applicationContext, 'getChromiumBrowser') - .mockImplementation(() => { - return null; - }); - - let error; - try { - await generatePaperServiceAddressPagePdf({ - applicationContext, - contactData: { - name: - 'Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons', - }, - }); - } catch (err) { - error = err; - } - - expect(error).toBeDefined(); - expect(chromiumBrowserMock.close).not.toHaveBeenCalled(); - }); - - it('requires permissions', async () => { - isAuthorized.mockReturnValue(false); - let result, error; - try { - result = await generatePaperServiceAddressPagePdf({ - applicationContext, - contactData: { - name: - 'Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons', - }, - }); - } catch (err) { - error = err; - } - expect(result).not.toBeDefined(); - expect(error.message).toEqual('Unauthorized'); - }); - - it('handles exceptions gracefully', async () => { - jest.spyOn(chromiumBrowserMock, 'newPage').mockImplementation(() => { - throw new Error('page problem'); - }); - let error; - try { - await generatePaperServiceAddressPagePdf({ - applicationContext, - contactData: { - name: - 'Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons', - }, - }); - } catch (err) { - error = err; - } - expect(error).toBeDefined(); - expect(applicationContext.logger.error).toHaveBeenCalled(); - expect(chromiumBrowserMock.close).toHaveBeenCalled(); - }); - - it('returns the pdf buffer produced by chromium', async () => { - const result = await generatePaperServiceAddressPagePdf({ - applicationContext, - caseEntity: { - ...MOCK_CASE, - contactData: { - name: - 'Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons', - }, - }, - }); - - expect(result).toBeDefined(); - }); -}); diff --git a/shared/src/business/useCaseHelper/courtIssuedDocument/paperServiceAddressPage.pug b/shared/src/business/useCaseHelper/courtIssuedDocument/paperServiceAddressPage.pug deleted file mode 100644 index 2db17686362..00000000000 --- a/shared/src/business/useCaseHelper/courtIssuedDocument/paperServiceAddressPage.pug +++ /dev/null @@ -1,23 +0,0 @@ -doctype html -html(lang="en") - head - meta(charset="utf-8") - title Address Page - style= css - body - .docket Docket #{docketNumberWithSuffix} - .address - | #{name} - br - | #{address1} - br - if address2 - | #{address2} - br - if address3 - | #{address3} - br - | #{city}, #{state} #{postalCode} - if !address3 - br - | #{countryName} \ No newline at end of file diff --git a/shared/src/business/useCaseHelper/pendingItems/fetchPendingItems.test.js b/shared/src/business/useCaseHelper/pendingItems/fetchPendingItems.test.js index 4aae8ca5029..3d3d3e4fb0e 100644 --- a/shared/src/business/useCaseHelper/pendingItems/fetchPendingItems.test.js +++ b/shared/src/business/useCaseHelper/pendingItems/fetchPendingItems.test.js @@ -137,7 +137,7 @@ describe('fetchPendingItems', () => { eventCode: 'PSDE', pending: true, processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, ]); diff --git a/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.js b/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.js index 72634dedc0e..34d259be8c7 100644 --- a/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.js +++ b/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.js @@ -7,14 +7,18 @@ * @param {string} providers.file the file to save * @returns {string} the url to the file */ -exports.saveFileAndGenerateUrl = async ({ applicationContext, file }) => { +exports.saveFileAndGenerateUrl = async ({ + applicationContext, + file, + useTempBucket = false, +}) => { const fileId = applicationContext.getUniqueId(); await applicationContext.getPersistenceGateway().saveDocumentFromLambda({ applicationContext, document: file, documentId: fileId, - useTempBucket: true, + useTempBucket, }); const { @@ -22,7 +26,7 @@ exports.saveFileAndGenerateUrl = async ({ applicationContext, file }) => { } = await applicationContext.getPersistenceGateway().getDownloadPolicyUrl({ applicationContext, documentId: fileId, - useTempBucket: true, + useTempBucket, }); return { fileId, url }; }; diff --git a/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.test.js b/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.test.js index bf50acf477e..624b11576ee 100644 --- a/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.test.js +++ b/shared/src/business/useCaseHelper/saveFileAndGenerateUrl.test.js @@ -2,7 +2,7 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { saveFileAndGenerateUrl } = require('./saveFileAndGenerateUrl'); describe('saveFileAndGenerateUrl', () => { - it('saves the file to s3 and returns the file ID and url to the file', async () => { + it('saves the file to s3 and return the file ID and url to the file', async () => { const mockUUID = '12345'; const mockPdfUrlAndId = { fileId: mockUUID, url: 'www.example.com' }; applicationContext @@ -17,8 +17,9 @@ describe('saveFileAndGenerateUrl', () => { expect(applicationContext.getUniqueId).toBeCalled(); expect( - applicationContext.getPersistenceGateway().saveDocumentFromLambda, - ).toBeCalled(); + applicationContext.getPersistenceGateway().saveDocumentFromLambda.mock + .calls[0][0], + ).toMatchObject({ useTempBucket: false }); expect( applicationContext.getPersistenceGateway().getDownloadPolicyUrl, ).toBeCalled(); diff --git a/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.js b/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.js index 1b64f5fb608..72ee26e9827 100644 --- a/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.js +++ b/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.js @@ -1,5 +1,3 @@ -const { PDFDocument } = require('pdf-lib'); - exports.appendPaperServiceAddressPageToPdf = async ({ applicationContext, caseEntity, @@ -15,6 +13,7 @@ exports.appendPaperServiceAddressPageToPdf = async ({ await exports.copyToNewPdf({ addressPages, + applicationContext, newPdfDoc, noticeDoc, }); @@ -28,21 +27,26 @@ exports.getAddressPages = async ({ const addressPages = []; for (let party of servedParties.paper) { addressPages.push( - await applicationContext - .getUseCaseHelpers() - .generatePaperServiceAddressPagePdf({ - applicationContext, - contactData: party, - docketNumberWithSuffix: `${caseEntity.docketNumber}${ - caseEntity.docketNumberSuffix || '' - }`, - }), + await applicationContext.getDocumentGenerators().addressLabelCoverSheet({ + applicationContext, + data: { + ...party, + docketNumberWithSuffix: caseEntity.docketNumberWithSuffix, + }, + }), ); } return addressPages; }; -exports.copyToNewPdf = async ({ addressPages, newPdfDoc, noticeDoc }) => { +exports.copyToNewPdf = async ({ + addressPages, + applicationContext, + newPdfDoc, + noticeDoc, +}) => { + const { PDFDocument } = await applicationContext.getPdfLib(); + for (let addressPage of addressPages) { const addressPageDoc = await PDFDocument.load(addressPage); let copiedPages = await newPdfDoc.copyPages( diff --git a/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.test.js b/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.test.js index 526c3de8713..798a3c0f97b 100644 --- a/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.test.js +++ b/shared/src/business/useCaseHelper/service/appendPaperServiceAddressPageToPdf.test.js @@ -16,8 +16,8 @@ const testPdfDoc = testPdfDocBytes(); describe('appendPaperServiceAddressPageToPdf', () => { applicationContext - .getUseCaseHelpers() - .generatePaperServiceAddressPagePdf.mockResolvedValue(testPdfDoc); + .getDocumentGenerators() + .addressLabelCoverSheet.mockResolvedValue(testPdfDoc); it('should generate address page for each paper service party and combine into single pdf', async () => { const newPdfDoc = await PDFDocument.create(); diff --git a/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.js b/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.js index 04aab1cb469..3b96f454fe8 100644 --- a/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.js +++ b/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.js @@ -2,6 +2,7 @@ const { reactTemplateGenerator, } = require('../../utilities/generateHTMLTemplateForPDF/reactTemplateGenerator'); const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); exports.sendServedPartiesEmails = async ({ applicationContext, @@ -29,7 +30,7 @@ exports.sendServedPartiesEmails = async ({ .formatNow('MMMM D, YYYY'); //serve every document on IRS superuser if case has been served to the IRS - if (caseEntity.status !== Case.STATUS_TYPES.new) { + if (caseEntity.status !== CASE_STATUS_TYPES.new) { servedParties.electronic.push({ email: applicationContext.getIrsSuperuserEmail(), name: 'IRS', diff --git a/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.test.js b/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.test.js index 04d6c845215..64042fb37fd 100644 --- a/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.test.js +++ b/shared/src/business/useCaseHelper/service/sendServedPartiesEmails.test.js @@ -1,7 +1,7 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); -const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { sendServedPartiesEmails } = require('./sendServedPartiesEmails'); describe('sendServedPartiesEmails', () => { @@ -18,7 +18,7 @@ describe('sendServedPartiesEmails', () => { caseCaption: 'A Caption', docketNumber: '123-20', docketRecord: [{ documentId: '0c745ceb-364a-4a1e-83b0-061f6f96a360' }], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, documentEntity: { documentId: '0c745ceb-364a-4a1e-83b0-061f6f96a360', @@ -53,7 +53,7 @@ describe('sendServedPartiesEmails', () => { caseCaption: 'A Caption', docketNumber: '123-20', docketRecord: [{ documentId: '0c745ceb-364a-4a1e-83b0-061f6f96a360' }], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentEntity: { documentId: '0c745ceb-364a-4a1e-83b0-061f6f96a360', @@ -84,7 +84,7 @@ describe('sendServedPartiesEmails', () => { caseCaption: 'A Caption', docketNumber: '123-20', docketRecord: [{ documentId: '0c745ceb-364a-4a1e-83b0-061f6f96a360' }], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, documentEntity: { documentId: '0c745ceb-364a-4a1e-83b0-061f6f96a360', diff --git a/shared/src/business/useCaseHelper/standingPretrialNotice/generateStandingPretrialNoticeTemplate.js b/shared/src/business/useCaseHelper/standingPretrialNotice/generateStandingPretrialNoticeTemplate.js deleted file mode 100644 index d8c65c70052..00000000000 --- a/shared/src/business/useCaseHelper/standingPretrialNotice/generateStandingPretrialNoticeTemplate.js +++ /dev/null @@ -1,78 +0,0 @@ -const template = require('./standingPretrialNotice.pug_'); -const { - createISODateString, - formatDateString, - formatNow, -} = require('../../utilities/DateHandler'); -const { - generateHTMLTemplateForPDF, -} = require('../../utilities/generateHTMLTemplateForPDF'); -const { Case } = require('../../entities/cases/Case'); - -/** - * HTML template generator for a Standing Pretrial Notice - * - * @param {object} deconstructed function arguments - * @param {object} deconstructed.applicationContext object that contains all the context specific methods - * @param {object} deconstructed.content content to be injected into the template - * @returns {string} hydrated HTML content in string form - */ -const generateStandingPretrialNoticeTemplate = async ({ - applicationContext, - content, -}) => { - const { caseCaption, docketNumberWithSuffix, trialInfo } = content; - - const pug = applicationContext.getPug(); - - const headerDate = formatNow('MMMM D, YYYY'); - const footerDate = formatNow('MMDDYYYY'); - const trialStartTimeIso = createISODateString(trialInfo.startTime, 'HH:mm'); - trialInfo.startTime = formatDateString(trialStartTimeIso, 'hh:mm A'); - trialInfo.startDay = formatDateString(trialInfo.startDate, 'dddd'); - trialInfo.fullStartDate = formatDateString( - trialInfo.startDate, - 'dddd, MMMM D, YYYY', - ); - trialInfo.startDate = formatDateString(trialInfo.startDate, 'MMDDYYYY'); - - let caseTitle = Case.getCaseTitle(caseCaption); - let caseCaptionExtension = ''; - if (caseTitle !== caseCaption) { - caseTitle += ', '; - caseCaptionExtension = caseCaption.replace(caseTitle, ''); - } - - let respondentContactText = 'not available at this time'; - if (trialInfo.irsPractitioners && trialInfo.irsPractitioners.length) { - const firstRespondent = trialInfo.irsPractitioners[0]; - respondentContactText = `${firstRespondent.name} (${firstRespondent.contact.phone})`; - } - trialInfo.respondentContactText = respondentContactText; - - const compiledFunction = pug.compile(template); - const main = compiledFunction({ - caseCaptionExtension, - caseTitle, - docketNumberWithSuffix, - footerDate, - headerDate, - trialInfo, - }); - - const templateContent = { - caseCaptionWithPostfix: `${caseCaption} ${Case.CASE_CAPTION_POSTFIX}`, - docketNumberWithSuffix, - main, - }; - - return await generateHTMLTemplateForPDF({ - applicationContext, - content: templateContent, - options: { overwriteMain: true }, - }); -}; - -module.exports = { - generateStandingPretrialNoticeTemplate, -}; diff --git a/shared/src/business/useCaseHelper/standingPretrialNotice/generateStandingPretrialNoticeTemplate.test.js b/shared/src/business/useCaseHelper/standingPretrialNotice/generateStandingPretrialNoticeTemplate.test.js deleted file mode 100644 index 8e9f05a21ae..00000000000 --- a/shared/src/business/useCaseHelper/standingPretrialNotice/generateStandingPretrialNoticeTemplate.test.js +++ /dev/null @@ -1,105 +0,0 @@ -const { - generateStandingPretrialNoticeTemplate, -} = require('./generateStandingPretrialNoticeTemplate'); - -const createApplicationContext = require('../../../../../web-api/src/applicationContext'); -const applicationContext = createApplicationContext({}); - -describe('generateStandingPretrialNoticeTemplate', () => { - const caseDetail = { - caseCaption: 'Test Case Caption', - docketNumber: '123-45', - docketNumberSuffix: 'S', - }; - - it('Returns HTML with the given case and trial session data', async () => { - const result = await generateStandingPretrialNoticeTemplate({ - applicationContext, - content: { - caseCaption: caseDetail.caseCaption, - docketNumberWithSuffix: - caseDetail.docketNumber + (caseDetail.docketNumberSuffix || ''), - trialInfo: { - address1: 'Address 1', - address2: 'Address 2', - city: 'City', - courthouseName: 'Courthouse Name', - irsPractitioners: [ - { - contact: { - phone: '123-456-7890', - }, - name: 'Guy Fieri', - }, - ], - judge: { name: 'Test Judge' }, - postalCode: '12345', - startDate: '2020-02-02T05:00:00.000Z', - startTime: '10:00', - state: 'ST', - }, - }, - }); - - expect(result.indexOf('')).toBe(0); - expect(result.indexOf('Test Case Caption')).toBeGreaterThan(-1); - expect(result.indexOf('123-45S')).toBeGreaterThan(-1); - expect(result.indexOf('Courthouse Name')).toBeGreaterThan(-1); - expect(result.indexOf('Address 1')).toBeGreaterThan(-1); - expect(result.indexOf('Address 2')).toBeGreaterThan(-1); - expect(result.indexOf('City')).toBeGreaterThan(-1); - expect(result.indexOf('ST')).toBeGreaterThan(-1); - expect(result.indexOf('STANDING PRETRIAL NOTICE')).toBeGreaterThan(-1); - expect(result.indexOf('10:00 AM')).toBeGreaterThan(-1); - expect(result.indexOf('Sunday, February 2, 2020')).toBeGreaterThan(-1); - expect( - result.indexOf('Their name and phone number is Guy Fieri (123-456-7890)'), - ).toBeGreaterThan(-1); - expect(result.indexOf('(Signed) Test Judge')).toBeGreaterThan(-1); - }); - - it('does not return respondent information if the irsPractitioners field is NOT an array of respondent objects', async () => { - const result = await generateStandingPretrialNoticeTemplate({ - applicationContext, - content: { - caseCaption: caseDetail.caseCaption, - docketNumberWithSuffix: - caseDetail.docketNumber + (caseDetail.docketNumberSuffix || ''), - trialInfo: { - address1: 'Address 1', - address2: 'Address 2', - city: 'City', - courthouseName: 'Courthouse Name', - irsPractitioners: { - // will be ignored since it's not an array - contact: { - phone: '123-456-7890', - }, - name: 'Guy Fieri', - }, - judge: { name: 'Test Judge' }, - postalCode: '12345', - startDate: '2020-02-02T05:00:00.000Z', - startTime: '10:00', - state: 'ST', - }, - }, - }); - - expect(result.indexOf('')).toBe(0); - expect(result.indexOf('Test Case Caption')).toBeGreaterThan(-1); - expect(result.indexOf('123-45S')).toBeGreaterThan(-1); - expect(result.indexOf('Courthouse Name')).toBeGreaterThan(-1); - expect(result.indexOf('Address 1')).toBeGreaterThan(-1); - expect(result.indexOf('Address 2')).toBeGreaterThan(-1); - expect(result.indexOf('City')).toBeGreaterThan(-1); - expect(result.indexOf('ST')).toBeGreaterThan(-1); - expect(result.indexOf('STANDING PRETRIAL NOTICE')).toBeGreaterThan(-1); - expect(result.indexOf('10:00 AM')).toBeGreaterThan(-1); - expect(result.indexOf('Sunday, February 2, 2020')).toBeGreaterThan(-1); - expect( - result.indexOf('Their name and phone number is Guy Fieri (123-456-7890)'), - ).toEqual(-1); - expect(result.indexOf('(Signed) Test Judge')).toBeGreaterThan(-1); - }); -}); diff --git a/shared/src/business/useCaseHelper/standingPretrialNotice/standingPretrialNotice.pug b/shared/src/business/useCaseHelper/standingPretrialNotice/standingPretrialNotice.pug deleted file mode 100644 index cd8f5bc25cb..00000000000 --- a/shared/src/business/useCaseHelper/standingPretrialNotice/standingPretrialNotice.pug +++ /dev/null @@ -1,87 +0,0 @@ -mixin case-info-box - .info-box - .info-box-header.text-normal - p.text-center - | This case is set for the trial session beginning at - b #{trialInfo.startTime} - | on - b #{trialInfo.fullStartDate} - | . - - .info-box-content - p.text-center - | #{trialInfo.courthouseName} - br - | #{trialInfo.address1} - br - if trialInfo.address2 - | #{trialInfo.address2} - br - if trialInfo.address3 - | #{trialInfo.address3} - br - | #{trialInfo.city}, #{trialInfo.state} #{trialInfo.postalCode} - -.court-header - .us-tax-court-seal - h1 United States Tax Court - p.heading-address.margin-top-0 - | 400 Second Street, N.W. - br - | Washington, D.C. 20217 - -.case-information - #case-caption - p #{caseTitle} - p.space #{caseCaptionExtension} - .clear - #caption - p.space.margin-top-0 v. - p Commissioner of Internal Revenue - p.space Respondent - #docket-number Docket Number #{docketNumberWithSuffix} - .clear - -h1(style='margin-bottom:39px') STANDING PRETRIAL NOTICE - -+case-info-box - -ul.notice-notes(style='margin-bottom:225px') - li - b WHAT TO DO NOW - ul - li Call respondent’s counsel (the IRS lawyer) as soon as possible. Their name and phone number is #{trialInfo.respondentContactText}. - ul - li Tell them if English is not your first language. - li Send them copies of documents that you think help your case. - li Find out whether you will need to go to trial or if you can settle your case. - li Visit the Tax Court website at www.ustaxcourt.gov for more information about the process and Frequently Asked Questions. - -h3.text-center.text-bold(style='page-break-after:always') Served #{footerDate} - -+case-info-box - -ul.notice-notes(style='margin-bottom:100px') - li - b WHAT TO DO BEFORE TRIAL - ul - li Talk to the IRS lawyer about what facts and documents you can both stipulate (agree) to. Facts might include your name, address, and the tax year(s) involved. Documents might include the notice or your tax returns. - li For documents you cannot agree on, bring 3 copies to trial. A photocopier may not be available at the courthouse. Only documents presented at trial will be part of the case record. The Tax Court is not part of the IRS. If you gave something to the IRS before, bring a copy with you! - li Consider filing a “pretrial memorandum” because it is helpful to the Judge. A form you can use is attached. - li You can send an update to the Court a week in advance if you need to by filing a Final Status Report, available at https://www.ustaxcourt.gov/FinalStatusReport/. - li Your case may not be tried on #{trialInfo.fullStartDate} because trial sessions can last more than one day. If you have witnesses that can only be there on #{trialInfo.startDay}, please put that in your pretrial memorandum or Final Status Report. - li(style='margin-top:30px') - b WHAT TO DO THE DAY OF THE TRIAL SESSION - ul - li Arrive 1 hour early because you will have to go through security. Bring a government-issued photo ID. - li Don’t forget copies of your documents. - li Be prepared for trial. Cases will not be continued (postponed) other than in unusual situations. - li(style='margin-top:30px') Find out more at - a(href="http://www.ustaxcourt.gov") www.ustaxcourt.gov - | or call 202-521-0700. - -.signature.text-center - p - | (Signed) #{trialInfo.judge.name} - br - | Trial Judge diff --git a/shared/src/business/useCases/addCoversheetInteractor.js b/shared/src/business/useCases/addCoversheetInteractor.js index 5c2f288d1c0..88f66b16fd5 100644 --- a/shared/src/business/useCases/addCoversheetInteractor.js +++ b/shared/src/business/useCases/addCoversheetInteractor.js @@ -1,8 +1,4 @@ -const { - generateCoverPagePdf, -} = require('../utilities/generateHTMLTemplateForPDF/generateCoverPagePdf'); const { Case } = require('../entities/cases/Case'); -const { PDFDocument } = require('pdf-lib'); /** * a helper function which assembles the correct data to be used in the generation of a PDF @@ -11,22 +7,23 @@ const { PDFDocument } = require('pdf-lib'); * @param {object} options.applicationContext the application context * @param {string} options.caseEntity the case entity associated with the document we are creating the cover for * @param {object} options.documentEntity the document entity we are creating the cover for + * @param {boolean} options.useInitialData whether to use the initial docket record suffix and case caption * @returns {object} the key/value pairs of computed strings */ exports.generateCoverSheetData = ({ applicationContext, caseEntity, documentEntity, + useInitialData, }) => { const isLodged = documentEntity.lodged; - const { isPaper } = documentEntity; + const { certificateOfService, isPaper } = documentEntity; const dateServedFormatted = (documentEntity.servedAt && - 'Served ' + - applicationContext - .getUtilities() - .formatDateString(documentEntity.servedAt, 'MMDDYYYY')) || + applicationContext + .getUtilities() + .formatDateString(documentEntity.servedAt, 'MMDDYYYY')) || ''; let dateReceivedFormatted; @@ -54,7 +51,15 @@ exports.generateCoverSheetData = ({ .formatDateString(documentEntity.filingDate, 'MMDDYYYY')) || ''; - const caseCaption = caseEntity.caseCaption || Case.getCaseCaption(caseEntity); + const caseCaptionToUse = useInitialData + ? caseEntity.initialCaption + : caseEntity.caseCaption; + + const docketNumberSuffixToUse = useInitialData + ? caseEntity.initialDocketNumberSuffix.replace('_', '') + : caseEntity.docketNumberSuffix; + + const caseCaption = caseCaptionToUse || Case.getCaseCaption(caseEntity); let caseTitle = applicationContext.getCaseTitle(caseCaption); let caseCaptionExtension = ''; if (caseTitle !== caseCaption) { @@ -69,22 +74,19 @@ exports.generateCoverSheetData = ({ } const docketNumberWithSuffix = - caseEntity.docketNumber + (caseEntity.docketNumberSuffix || ''); + caseEntity.docketNumber + (docketNumberSuffixToUse || ''); const coverSheetData = { caseCaptionExtension, caseTitle, - certificateOfService: - documentEntity.certificateOfService === true - ? 'Certificate of Service' - : '', + certificateOfService, dateFiledLodged: dateFiledFormatted, dateFiledLodgedLabel: isLodged ? 'Lodged' : 'Filed', dateReceived: dateReceivedFormatted, dateServed: dateServedFormatted, docketNumber: `Docket Number: ${docketNumberWithSuffix}`, documentTitle, - electronicallyFiled: documentEntity.isPaper ? '' : 'Electronically Filed', + electronicallyFiled: !documentEntity.isPaper, mailingDate: documentEntity.mailingDate || '', }; return coverSheetData; @@ -104,22 +106,29 @@ exports.addCoverToPdf = async ({ caseEntity, documentEntity, pdfData, + replaceCoversheet = false, + useInitialData = false, }) => { const coverSheetData = exports.generateCoverSheetData({ applicationContext, caseEntity, documentEntity, + useInitialData, }); + const { PDFDocument } = await applicationContext.getPdfLib(); + const pdfDoc = await PDFDocument.load(pdfData); // allow GC to clear original loaded pdf data pdfData = null; - const coverPagePdf = await generateCoverPagePdf({ - applicationContext, - content: coverSheetData, - }); + const coverPagePdf = await applicationContext + .getDocumentGenerators() + .coverSheet({ + applicationContext, + data: coverSheetData, + }); const coverPageDocument = await PDFDocument.load(coverPagePdf); const coverPageDocumentPages = await pdfDoc.copyPages( @@ -127,9 +136,20 @@ exports.addCoverToPdf = async ({ coverPageDocument.getPageIndices(), ); - pdfDoc.insertPage(0, coverPageDocumentPages[0]); + if (replaceCoversheet) { + pdfDoc.removePage(0); + pdfDoc.insertPage(0, coverPageDocumentPages[0]); + } else { + pdfDoc.insertPage(0, coverPageDocumentPages[0]); + } + + const newPdfData = await pdfDoc.save(); + const numberOfPages = pdfDoc.getPages().length; - return await pdfDoc.save(); + return { + numberOfPages, + pdfData: newPdfData, + }; }; /** @@ -144,6 +164,8 @@ exports.addCoversheetInteractor = async ({ applicationContext, caseId, documentId, + replaceCoversheet = false, + useInitialData = false, }) => { const caseRecord = await applicationContext .getPersistenceGateway() @@ -154,9 +176,8 @@ exports.addCoversheetInteractor = async ({ const caseEntity = new Case(caseRecord, { applicationContext }); - const documentEntity = caseEntity.documents.find( - document => document.documentId === documentId, - ); + const documentEntity = caseEntity.getDocumentById({ documentId }); + const docketRecordEntity = caseEntity.getDocketRecordByDocumentId(documentId); let pdfData; try { @@ -173,22 +194,35 @@ exports.addCoversheetInteractor = async ({ throw err; } - const newPdfData = await exports.addCoverToPdf({ + const { numberOfPages, pdfData: newPdfData } = await exports.addCoverToPdf({ applicationContext, caseEntity, documentEntity, pdfData, + replaceCoversheet, + useInitialData, }); documentEntity.setAsProcessingStatusAsCompleted(); + documentEntity.setNumberOfPages(numberOfPages); - await applicationContext - .getPersistenceGateway() - .updateDocumentProcessingStatus({ + await applicationContext.getPersistenceGateway().updateDocument({ + applicationContext, + caseId, + document: documentEntity.validate().toRawObject(), + documentId, + }); + + if (docketRecordEntity) { + docketRecordEntity.setNumberOfPages(numberOfPages); + + await applicationContext.getPersistenceGateway().updateDocketRecord({ applicationContext, caseId, - documentId, + docketRecord: docketRecordEntity.validate().toRawObject(), + docketRecordId: docketRecordEntity.docketRecordId, }); + } await applicationContext.getPersistenceGateway().saveDocumentFromLambda({ applicationContext, diff --git a/shared/src/business/useCases/addCoversheetInteractor.test.js b/shared/src/business/useCases/addCoversheetInteractor.test.js index 73f8622195b..749d5ea7571 100644 --- a/shared/src/business/useCases/addCoversheetInteractor.test.js +++ b/shared/src/business/useCases/addCoversheetInteractor.test.js @@ -5,12 +5,7 @@ const { generateCoverSheetData, } = require('./addCoversheetInteractor.js'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); - -jest.mock('../utilities/generateHTMLTemplateForPDF/generateCoverPagePdf'); -const { - generateCoverPagePdf, -} = require('../utilities/generateHTMLTemplateForPDF/generateCoverPagePdf'); +const { PARTY_TYPES } = require('../entities/EntityConstants'); describe('addCoversheetInteractor', () => { const testAssetsPath = path.join(__dirname, '../../../test-assets/'); @@ -39,10 +34,10 @@ describe('addCoversheetInteractor', () => { filingDate: '2019-04-19T14:45:15.595Z', isPaper: false, processingStatus: 'pending', - userId: 'petitionsclerk', + userId: '02323349-87fe-4d29-91fe-8dd6916d2fda', }, ], - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }; const optionalTestingCaseData = { @@ -60,6 +55,7 @@ describe('addCoversheetInteractor', () => { addToCoversheet: true, additionalInfo: 'Additional Info Something', certificateOfService: true, + certificateOfServiceDate: '2019-04-20', documentId: 'b6b81f4d-1e47-423a-8caf-6d2fdc3d3858', documentType: 'Motion for Entry of Order that Undenied Allegations be Deemed Admitted Pursuant to Rule 37(c)', @@ -68,14 +64,12 @@ describe('addCoversheetInteractor', () => { lodged: true, }, ], - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }; beforeAll(() => { jest.setTimeout(30000); - generateCoverPagePdf.mockImplementation(testPdfDocBytes); - applicationContext.getStorageClient().getObject.mockReturnValue({ promise: async () => ({ Body: testPdfDoc, @@ -96,11 +90,84 @@ describe('addCoversheetInteractor', () => { await addCoversheetInteractor(params); + expect( + applicationContext.getDocumentGenerators().coverSheet, + ).toHaveBeenCalled(); expect( applicationContext.getPersistenceGateway().saveDocumentFromLambda, ).toHaveBeenCalled(); }); + it('replaces the cover page on a document', async () => { + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(testingCaseData); + + const params = { + applicationContext, + caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentId: 'a6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + replaceCoversheet: true, + }; + + await addCoversheetInteractor(params); + + expect( + applicationContext.getDocumentGenerators().coverSheet, + ).toHaveBeenCalled(); + expect( + applicationContext.getPersistenceGateway().saveDocumentFromLambda, + ).toHaveBeenCalled(); + }); + + it("updates the document's page numbers", async () => { + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(testingCaseData); + + const params = { + applicationContext, + caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentId: 'a6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + }; + + await addCoversheetInteractor(params); + + expect( + applicationContext.getPersistenceGateway().updateDocument, + ).toHaveBeenCalled(); + }); + + it("updates the document and docket record's page numbers", async () => { + applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ + ...testingCaseData, + docketRecord: [ + { + description: 'Test Docket Record', + documentId: 'a6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + eventCode: 'O', + filingDate: new Date('2000-01-01').toISOString(), + index: 0, + }, + ], + }); + + const params = { + applicationContext, + caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentId: 'a6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + }; + + await addCoversheetInteractor(params); + + expect( + applicationContext.getPersistenceGateway().updateDocument, + ).toHaveBeenCalled(); + expect( + applicationContext.getPersistenceGateway().updateDocketRecord, + ).toHaveBeenCalled(); + }); + it('adds a cover page to a pdf document with optional data', async () => { applicationContext .getPersistenceGateway() @@ -143,7 +210,7 @@ describe('addCoversheetInteractor', () => { lodged: true, }, ], - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }; it('displays Certificate of Service when the document is filed with a certificate of service', async () => { @@ -166,7 +233,7 @@ describe('addCoversheetInteractor', () => { }, }); - expect(result.certificateOfService).toEqual('Certificate of Service'); + expect(result.certificateOfService).toEqual(true); }); it('does NOT display Certificate of Service when the document is filed without a certificate of service', async () => { @@ -188,7 +255,7 @@ describe('addCoversheetInteractor', () => { isPaper: true, }, }); - expect(result.certificateOfService).toEqual(''); + expect(result.certificateOfService).toEqual(false); }); it('generates correct filed date', async () => { @@ -378,7 +445,7 @@ describe('addCoversheetInteractor', () => { expect(result.dateReceived).toEqual(''); }); - it('displays the date served if present in MMDDYYYY format along with a Served label', async () => { + it('displays the date served if present in MMDDYYYY format', async () => { const result = generateCoverSheetData({ applicationContext, caseEntity: { @@ -400,7 +467,7 @@ describe('addCoversheetInteractor', () => { }, }); - expect(result.dateServed).toEqual('Served 04/20/2019'); + expect(result.dateServed).toEqual('04/20/2019'); }); it('does not display the service date if servedAt is not present', async () => { @@ -496,7 +563,7 @@ describe('addCoversheetInteractor', () => { }, }); - expect(result.electronicallyFiled).toEqual('Electronically Filed'); + expect(result.electronicallyFiled).toEqual(true); }); it('does NOT display Electronically Filed when the document is filed by paper', async () => { @@ -519,7 +586,7 @@ describe('addCoversheetInteractor', () => { }, }); - expect(result.electronicallyFiled).toEqual(''); + expect(result.electronicallyFiled).toEqual(false); }); it('returns the mailing date if present', async () => { @@ -637,5 +704,33 @@ describe('addCoversheetInteractor', () => { expect(result.caseCaptionExtension).toEqual(''); }); + + it('preserves the original case caption and docket number when the useInitialData is true', () => { + const result = generateCoverSheetData({ + applicationContext, + caseEntity: { + ...caseData, + caseCaption: 'Janie Petitioner, Petitioner', + docketNumberSuffix: 'S', + initialCaption: 'Janie and Jackie Petitioner, Petitioners', + initialDocketNumberSuffix: 'Z', + }, + documentEntity: { + ...testingCaseData.documents[0], + addToCoversheet: true, + additionalInfo: 'Additional Info Something', + certificateOfService: true, + documentId: 'b6b81f4d-1e47-423a-8caf-6d2fdc3d3858', + documentType: + 'Motion for Entry of Order that Undenied Allegations be Deemed Admitted Pursuant to Rule 37(c)', + isPaper: false, + lodged: true, + }, + useInitialData: true, + }); + + expect(result.docketNumber).toEqual('Docket Number: 102-19Z'); + expect(result.caseTitle).toEqual('Janie and Jackie Petitioner, '); + }); }); }); diff --git a/shared/src/business/useCases/archiveDraftDocumentInteractor.test.js b/shared/src/business/useCases/archiveDraftDocumentInteractor.test.js index 8986ea0c33c..f909cd98f01 100644 --- a/shared/src/business/useCases/archiveDraftDocumentInteractor.test.js +++ b/shared/src/business/useCases/archiveDraftDocumentInteractor.test.js @@ -3,7 +3,7 @@ const { } = require('./archiveDraftDocumentInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('archiveDraftDocumentInteractor', () => { it('returns an unauthorized error on non petitionsclerk users', async () => { @@ -20,7 +20,7 @@ describe('archiveDraftDocumentInteractor', () => { it('expect the updated case to contain the archived document', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }); applicationContext .getPersistenceGateway() diff --git a/shared/src/business/useCases/blockCaseFromTrialInteractor.test.js b/shared/src/business/useCases/blockCaseFromTrialInteractor.test.js index ee9bd830af5..715748fd1b9 100644 --- a/shared/src/business/useCases/blockCaseFromTrialInteractor.test.js +++ b/shared/src/business/useCases/blockCaseFromTrialInteractor.test.js @@ -3,12 +3,12 @@ const { } = require('./blockCaseFromTrialInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('blockCaseFromTrialInteractor', () => { beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); applicationContext diff --git a/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.js b/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.js index 6cb31cf71b7..05a8159b3a4 100644 --- a/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.js +++ b/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.js @@ -3,8 +3,8 @@ const { ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); const { Case } = require('../../entities/cases/Case'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); /** * deleteCounselFromCaseInteractor @@ -42,9 +42,9 @@ exports.deleteCounselFromCaseInteractor = async ({ const caseEntity = new Case(caseToUpdate, { applicationContext }); - if (userToDelete.role === User.ROLES.privatePractitioner) { + if (userToDelete.role === ROLES.privatePractitioner) { caseEntity.removePrivatePractitioner(userToDelete); - } else if (userToDelete.role === User.ROLES.irsPractitioner) { + } else if (userToDelete.role === ROLES.irsPractitioner) { caseEntity.removeIrsPractitioner(userToDelete); } else { throw new Error('User is not a practitioner'); diff --git a/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.test.js b/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.test.js index 67eb929322d..d1ff01fe585 100644 --- a/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.test.js +++ b/shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor.test.js @@ -5,27 +5,50 @@ const { deleteCounselFromCaseInteractor, } = require('./deleteCounselFromCaseInteractor'); const { MOCK_CASE } = require('../../../test/mockCase.js'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('deleteCounselFromCaseInteractor', () => { const mockPrivatePractitioners = [ - { role: User.ROLES.privatePractitioner, userId: '456' }, - { role: User.ROLES.privatePractitioner, userId: '789' }, - { role: User.ROLES.privatePractitioner, userId: '012' }, + { + role: ROLES.privatePractitioner, + userId: '02f8a9cf-3bc8-4c91-a765-2f19013cd004', + }, + { + role: ROLES.privatePractitioner, + userId: '141d4c7c-4302-465d-89bd-3bc8ae16f07d', + }, + { + role: ROLES.privatePractitioner, + userId: '6de95584-fbf2-42d7-bd81-bf9e10633404', + }, ]; const mockIrsPractitioners = [ - { role: User.ROLES.irsPractitioner, userId: '654' }, - { role: User.ROLES.irsPractitioner, userId: '987' }, - { role: User.ROLES.irsPractitioner, userId: '210' }, + { + role: ROLES.irsPractitioner, + userId: '547f2148-3bb8-408b-bbaa-40d53f14f924', + }, + { + role: ROLES.irsPractitioner, + userId: 'bfd97089-cda0-45e0-8454-dd879023d0af', + }, + { + role: ROLES.irsPractitioner, + userId: '55c50d5d-b2eb-466e-9775-d0e1b464472d', + }, ]; - const mockPetitioners = [{ role: User.ROLES.petitioner, userId: '111' }]; + const mockPetitioners = [ + { + role: ROLES.petitioner, + userId: '835f072c-5ea1-493c-acb8-d67b05c96f85', + }, + ]; beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, - userId: '001', + role: ROLES.docketClerk, + userId: 'fb39f224-7985-438d-8327-2df162c20c8e', }); applicationContext @@ -49,14 +72,14 @@ describe('deleteCounselFromCaseInteractor', () => { it('returns an unauthorized error for a petitioner user', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); await expect( deleteCounselFromCaseInteractor({ applicationContext, caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - userIdToDelete: '789', + userIdToDelete: '141d4c7c-4302-465d-89bd-3bc8ae16f07d', }), ).rejects.toThrow('Unauthorized'); }); @@ -65,7 +88,7 @@ describe('deleteCounselFromCaseInteractor', () => { await deleteCounselFromCaseInteractor({ applicationContext, caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - userIdToDelete: '789', + userIdToDelete: '141d4c7c-4302-465d-89bd-3bc8ae16f07d', }); expect( @@ -80,7 +103,7 @@ describe('deleteCounselFromCaseInteractor', () => { await deleteCounselFromCaseInteractor({ applicationContext, caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - userIdToDelete: '987', + userIdToDelete: 'bfd97089-cda0-45e0-8454-dd879023d0af', }); expect( @@ -96,7 +119,7 @@ describe('deleteCounselFromCaseInteractor', () => { deleteCounselFromCaseInteractor({ applicationContext, caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - userIdToDelete: '111', + userIdToDelete: '835f072c-5ea1-493c-acb8-d67b05c96f85', }), ).rejects.toThrow('User is not a practitioner'); }); diff --git a/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.js b/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.js index 251bbe5ffe8..d7b4ed0df11 100644 --- a/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.js +++ b/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.js @@ -3,8 +3,8 @@ const { ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); const { Case } = require('../../entities/cases/Case'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); /** * updateCounselOnCaseInteractor @@ -50,12 +50,12 @@ exports.updateCounselOnCaseInteractor = async ({ const caseEntity = new Case(caseToUpdate, { applicationContext }); - if (userToUpdate.role === User.ROLES.privatePractitioner) { + if (userToUpdate.role === ROLES.privatePractitioner) { caseEntity.updatePrivatePractitioner({ userId: userToUpdate.userId, ...editableFields, }); - } else if (userToUpdate.role === User.ROLES.irsPractitioner) { + } else if (userToUpdate.role === ROLES.irsPractitioner) { caseEntity.updateIrsPractitioner({ userId: userToUpdate.userId, ...editableFields, diff --git a/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.test.js b/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.test.js index ad352987a21..b62f860ba42 100644 --- a/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.test.js +++ b/shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor.test.js @@ -7,35 +7,49 @@ const { const { IrsPractitioner } = require('../../entities/IrsPractitioner'); const { MOCK_CASE } = require('../../../test/mockCase.js'); const { PrivatePractitioner } = require('../../entities/PrivatePractitioner'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('updateCounselOnCaseInteractor', () => { const mockPrivatePractitioners = [ new PrivatePractitioner({ - role: User.ROLES.privatePractitioner, - userId: '456', + role: ROLES.privatePractitioner, + userId: 'e23e2d08-561b-4930-a2e0-1f342a481268', }), new PrivatePractitioner({ - role: User.ROLES.privatePractitioner, - userId: '789', + role: ROLES.privatePractitioner, + userId: '9d914ca2-7876-43a7-acfa-ccb645717e11', }), new PrivatePractitioner({ - role: User.ROLES.privatePractitioner, - userId: '012', + role: ROLES.privatePractitioner, + userId: '4cae261f-3653-4d2f-8d8c-31f03df62e54', }), ]; const mockIrsPractitioners = [ - new IrsPractitioner({ role: User.ROLES.irsPractitioner, userId: '654' }), - new IrsPractitioner({ role: User.ROLES.irsPractitioner, userId: '987' }), - new IrsPractitioner({ role: User.ROLES.irsPractitioner, userId: '210' }), + new IrsPractitioner({ + role: ROLES.irsPractitioner, + userId: '9a4390b3-9d1a-4987-b918-312675956bcc', + }), + new IrsPractitioner({ + role: ROLES.irsPractitioner, + userId: '76c86b6b-6aad-4128-8fa2-53c5735cc0af', + }), + new IrsPractitioner({ + role: ROLES.irsPractitioner, + userId: 'dd60c66f-2f82-4f8f-824a-d15a3e8e49a3', + }), ]; - const mockPetitioners = [{ role: User.ROLES.petitioner, userId: '111' }]; + const mockPetitioners = [ + { + role: ROLES.petitioner, + userId: 'aa335271-9a0f-4ad5-bcf1-3b89bd8b5dd6', + }, + ]; beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '001', }); applicationContext @@ -92,7 +106,7 @@ describe('updateCounselOnCaseInteractor', () => { userData: { representingPrimary: true, }, - userIdToUpdate: '789', + userIdToUpdate: '9d914ca2-7876-43a7-acfa-ccb645717e11', }), ).rejects.toThrow('Unauthorized'); }); @@ -106,7 +120,7 @@ describe('updateCounselOnCaseInteractor', () => { representingSecondary: false, serviceIndicator: 'Electronic', }, - userIdToUpdate: '789', + userIdToUpdate: '9d914ca2-7876-43a7-acfa-ccb645717e11', }); expect( @@ -123,7 +137,7 @@ describe('updateCounselOnCaseInteractor', () => { representingSecondary: false, serviceIndicator: 'Electronic', }, - userIdToUpdate: '987', + userIdToUpdate: '76c86b6b-6aad-4128-8fa2-53c5735cc0af', }); expect( @@ -141,13 +155,13 @@ describe('updateCounselOnCaseInteractor', () => { representingSecondary: false, serviceIndicator: 'Electronic', }, - userIdToUpdate: '987', + userIdToUpdate: '76c86b6b-6aad-4128-8fa2-53c5735cc0af', }); const updatedPractitioner = applicationContext .getPersistenceGateway() .updateCase.mock.calls[0][0].caseToUpdate.irsPractitioners.find( - p => p.userId === '987', + p => p.userId === '76c86b6b-6aad-4128-8fa2-53c5735cc0af', ); expect(updatedPractitioner.email).toBeUndefined(); expect(updatedPractitioner.representingPrimary).toBe(true); @@ -163,7 +177,7 @@ describe('updateCounselOnCaseInteractor', () => { userData: { email: 'petitioner@example.com', }, - userIdToUpdate: '111', + userIdToUpdate: 'aa335271-9a0f-4ad5-bcf1-3b89bd8b5dd6', }), ).rejects.toThrow('User is not a practitioner'); }); diff --git a/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.js b/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.js index c5c3f6b79af..66545234873 100644 --- a/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.js +++ b/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.js @@ -1,4 +1,4 @@ -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); /** * userIsAssociated * @@ -15,14 +15,12 @@ exports.userIsAssociated = ({ caseDetail, user }) => { return true; } - if ( - ![User.ROLES.irsPractitioner, User.ROLES.privatePractitioner].includes(role) - ) { + if (![ROLES.irsPractitioner, ROLES.privatePractitioner].includes(role)) { return false; } let association; - if (role === User.ROLES.irsPractitioner) { + if (role === ROLES.irsPractitioner) { association = 'irsPractitioners'; } else { association = 'privatePractitioners'; diff --git a/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.test.js b/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.test.js index e395750ac9f..e910a1589d0 100644 --- a/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.test.js +++ b/shared/src/business/useCases/caseAssociation/userIsAssociatedInteractor.test.js @@ -1,7 +1,7 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const { userIsAssociated } = require('./userIsAssociatedInteractor'); describe('userIsAssociated', () => { @@ -11,7 +11,7 @@ describe('userIsAssociated', () => { userId: 'abc-123', }; const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'abc-123', }; @@ -30,14 +30,14 @@ describe('userIsAssociated', () => { userId: 'def-321', }; const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'abc-123', }; const result = userIsAssociated({ applicationContext, caseDetail, user }); expect(result).toEqual(true); - user.role = User.ROLES.privatePractitioner; + user.role = ROLES.privatePractitioner; caseDetail.irsPractitioners = [{ userId: 'abc-123' }]; const result2 = userIsAssociated({ applicationContext, caseDetail, user }); @@ -54,7 +54,7 @@ describe('userIsAssociated', () => { userId: 'def-321', }; const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'abc-123', }; @@ -68,7 +68,7 @@ describe('userIsAssociated', () => { userId: 'def-321', }; const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'abc-123', }; diff --git a/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.js b/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.js index b1dda7b9f2f..a7ee273cff2 100644 --- a/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.js +++ b/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.js @@ -8,8 +8,8 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); /** * submitCaseAssociationRequestInteractor @@ -42,8 +42,8 @@ exports.submitCaseAssociationRequestInteractor = async ({ .getUserById({ applicationContext, userId: authorizedUser.userId }); const isPrivatePractitioner = - authorizedUser.role === User.ROLES.privatePractitioner; - const isIrsPractitioner = authorizedUser.role === User.ROLES.irsPractitioner; + authorizedUser.role === ROLES.privatePractitioner; + const isIrsPractitioner = authorizedUser.role === ROLES.irsPractitioner; if (isPrivatePractitioner) { return await associatePrivatePractitionerToCase({ diff --git a/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.test.js b/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.test.js index 3c2d4e378e1..aa279b1bb52 100644 --- a/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.test.js +++ b/shared/src/business/useCases/caseAssociationRequest/submitCaseAssociationRequestInteractor.test.js @@ -5,7 +5,7 @@ const { submitCaseAssociationRequestInteractor, } = require('./submitCaseAssociationRequestInteractor'); const { MOCK_CASE } = require('../../../test/mockCase.js'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('submitCaseAssociationRequest', () => { let caseRecord = { @@ -56,8 +56,8 @@ describe('submitCaseAssociationRequest', () => { it('should throw an error when not authorized', async () => { mockCurrentUser = { - name: 'Olivia Jade', - role: User.ROLES.adc, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.adc, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; @@ -72,8 +72,8 @@ describe('submitCaseAssociationRequest', () => { it('should not add mapping if already there', async () => { mockCurrentUser = { - name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; mockGetUserById = { @@ -87,8 +87,8 @@ describe('submitCaseAssociationRequest', () => { postalCode: '61234', state: 'IL', }, - name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; applicationContext @@ -120,8 +120,8 @@ describe('submitCaseAssociationRequest', () => { postalCode: '61234', state: 'IL', }, - name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; applicationContext @@ -144,8 +144,8 @@ describe('submitCaseAssociationRequest', () => { it('should add mapping for an irsPractitioner', async () => { mockCurrentUser = { - name: 'Olivia Jade', - role: User.ROLES.irsPractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.irsPractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; mockGetUserById = { @@ -159,8 +159,8 @@ describe('submitCaseAssociationRequest', () => { postalCode: '61234', state: 'IL', }, - name: 'Olivia Jade', - role: User.ROLES.irsPractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.irsPractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; applicationContext diff --git a/shared/src/business/useCases/caseAssociationRequest/submitPendingCaseAssociationRequestInteractor.test.js b/shared/src/business/useCases/caseAssociationRequest/submitPendingCaseAssociationRequestInteractor.test.js index 119a9514cd2..ceb6163b7e9 100644 --- a/shared/src/business/useCases/caseAssociationRequest/submitPendingCaseAssociationRequestInteractor.test.js +++ b/shared/src/business/useCases/caseAssociationRequest/submitPendingCaseAssociationRequestInteractor.test.js @@ -4,7 +4,7 @@ const { const { submitPendingCaseAssociationRequestInteractor, } = require('./submitPendingCaseAssociationRequestInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('submitPendingCaseAssociationRequest', () => { let caseRecord = { @@ -14,8 +14,8 @@ describe('submitPendingCaseAssociationRequest', () => { it('should throw an error when not authorized', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.adc, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.adc, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -30,13 +30,13 @@ describe('submitPendingCaseAssociationRequest', () => { it('should not add mapping if already associated', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); applicationContext diff --git a/shared/src/business/useCases/caseConsolidation/addConsolidatedCaseInteractor.test.js b/shared/src/business/useCases/caseConsolidation/addConsolidatedCaseInteractor.test.js index 6881c11a3a7..860b054ca26 100644 --- a/shared/src/business/useCases/caseConsolidation/addConsolidatedCaseInteractor.test.js +++ b/shared/src/business/useCases/caseConsolidation/addConsolidatedCaseInteractor.test.js @@ -5,7 +5,7 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); let mockCases; @@ -61,7 +61,7 @@ describe('addConsolidatedCaseInteractor', () => { }; applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }); applicationContext .getPersistenceGateway() @@ -84,7 +84,7 @@ describe('addConsolidatedCaseInteractor', () => { let error; applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); try { diff --git a/shared/src/business/useCases/caseConsolidation/removeConsolidatedCasesInteractor.test.js b/shared/src/business/useCases/caseConsolidation/removeConsolidatedCasesInteractor.test.js index 28beb64cd02..9ba873b6a67 100644 --- a/shared/src/business/useCases/caseConsolidation/removeConsolidatedCasesInteractor.test.js +++ b/shared/src/business/useCases/caseConsolidation/removeConsolidatedCasesInteractor.test.js @@ -5,7 +5,7 @@ const { removeConsolidatedCasesInteractor, } = require('./removeConsolidatedCasesInteractor'); const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); let mockCases; @@ -50,7 +50,7 @@ describe('removeConsolidatedCasesInteractor', () => { }; applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }); applicationContext .getPersistenceGateway() @@ -73,7 +73,7 @@ describe('removeConsolidatedCasesInteractor', () => { let error; applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); try { diff --git a/shared/src/business/useCases/caseDeadline/createCaseDeadlineInteractor.test.js b/shared/src/business/useCases/caseDeadline/createCaseDeadlineInteractor.test.js index 5f01152b3b3..46d33e894d3 100644 --- a/shared/src/business/useCases/caseDeadline/createCaseDeadlineInteractor.test.js +++ b/shared/src/business/useCases/caseDeadline/createCaseDeadlineInteractor.test.js @@ -8,7 +8,8 @@ const { MOCK_CASE, MOCK_CASE_WITHOUT_PENDING, } = require('../../../test/mockCase'); -const { Case } = require('../../entities/cases/Case'); +const { AUTOMATIC_BLOCKED_REASONS } = require('../../entities/EntityConstants'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -24,7 +25,7 @@ describe('createCaseDeadlineInteractor', () => { beforeEach(() => { user = new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -68,7 +69,7 @@ describe('createCaseDeadlineInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.dueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.dueDate, }); expect( applicationContext.getPersistenceGateway() @@ -92,7 +93,7 @@ describe('createCaseDeadlineInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, }); expect( applicationContext.getPersistenceGateway() diff --git a/shared/src/business/useCases/caseDeadline/deleteCaseDeadlineInteractor.test.js b/shared/src/business/useCases/caseDeadline/deleteCaseDeadlineInteractor.test.js index 8ce8233e252..d7211706180 100644 --- a/shared/src/business/useCases/caseDeadline/deleteCaseDeadlineInteractor.test.js +++ b/shared/src/business/useCases/caseDeadline/deleteCaseDeadlineInteractor.test.js @@ -4,8 +4,9 @@ const { const { deleteCaseDeadlineInteractor, } = require('./deleteCaseDeadlineInteractor'); -const { Case } = require('../../entities/cases/Case'); +const { AUTOMATIC_BLOCKED_REASONS } = require('../../entities/EntityConstants'); const { MOCK_CASE_WITHOUT_PENDING } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -30,7 +31,7 @@ describe('deleteCaseDeadlineInteractor', () => { beforeEach(() => { user = new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -100,7 +101,7 @@ describe('deleteCaseDeadlineInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.dueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.dueDate, }); expect( applicationContext.getPersistenceGateway() diff --git a/shared/src/business/useCases/caseDeadline/getAllCaseDeadlinesInteractor.test.js b/shared/src/business/useCases/caseDeadline/getAllCaseDeadlinesInteractor.test.js index 55016996119..e60e8d53379 100644 --- a/shared/src/business/useCases/caseDeadline/getAllCaseDeadlinesInteractor.test.js +++ b/shared/src/business/useCases/caseDeadline/getAllCaseDeadlinesInteractor.test.js @@ -4,6 +4,7 @@ const { const { getAllCaseDeadlinesInteractor, } = require('./getAllCaseDeadlinesInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); describe('getAllCaseDeadlinesInteractor', () => { @@ -61,7 +62,7 @@ describe('getAllCaseDeadlinesInteractor', () => { it('gets all the case deadlines and combines them with case data', async () => { const mockPetitionsClerk = new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); applicationContext.getCurrentUser.mockReturnValue(mockPetitionsClerk); diff --git a/shared/src/business/useCases/caseDeadline/getCaseDeadlinesForCaseInteractor.test.js b/shared/src/business/useCases/caseDeadline/getCaseDeadlinesForCaseInteractor.test.js index d35acbe8d8a..6847d585ae4 100644 --- a/shared/src/business/useCases/caseDeadline/getCaseDeadlinesForCaseInteractor.test.js +++ b/shared/src/business/useCases/caseDeadline/getCaseDeadlinesForCaseInteractor.test.js @@ -4,6 +4,7 @@ const { const { getCaseDeadlinesForCaseInteractor, } = require('./getCaseDeadlinesForCaseInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); describe('getCaseDeadlinesForCaseInteractor', () => { @@ -15,7 +16,7 @@ describe('getCaseDeadlinesForCaseInteractor', () => { const mockUser = new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); diff --git a/shared/src/business/useCases/caseDeadline/updateCaseDeadlineInteractor.test.js b/shared/src/business/useCases/caseDeadline/updateCaseDeadlineInteractor.test.js index 2a10fac8b49..f5ee742b19e 100644 --- a/shared/src/business/useCases/caseDeadline/updateCaseDeadlineInteractor.test.js +++ b/shared/src/business/useCases/caseDeadline/updateCaseDeadlineInteractor.test.js @@ -4,6 +4,7 @@ const { const { updateCaseDeadlineInteractor, } = require('./updateCaseDeadlineInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -34,7 +35,7 @@ describe('updateCaseDeadlineInteractor', () => { it('updates a case deadline', async () => { const mockPetitionsClerk = new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); applicationContext.environment.stage = 'local'; diff --git a/shared/src/business/useCases/caseInventoryReport/generatePrintableCaseInventoryReportInteractor.test.js b/shared/src/business/useCases/caseInventoryReport/generatePrintableCaseInventoryReportInteractor.test.js index e7d57c6f67c..055b0d8945c 100644 --- a/shared/src/business/useCases/caseInventoryReport/generatePrintableCaseInventoryReportInteractor.test.js +++ b/shared/src/business/useCases/caseInventoryReport/generatePrintableCaseInventoryReportInteractor.test.js @@ -4,12 +4,12 @@ const { const { generatePrintableCaseInventoryReportInteractor, } = require('./generatePrintableCaseInventoryReportInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('generatePrintableCaseInventoryReportInteractor', () => { it('calls generateCaseInventoryReportPdf function and returns result', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); applicationContext @@ -32,7 +32,7 @@ describe('generatePrintableCaseInventoryReportInteractor', () => { it('should throw an unauthorized error if the user does not have access', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); @@ -46,7 +46,7 @@ describe('generatePrintableCaseInventoryReportInteractor', () => { it('should throw an error if associatedJudge and status are not passed in', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); diff --git a/shared/src/business/useCases/caseInventoryReport/getCaseInventoryReportInteractor.test.js b/shared/src/business/useCases/caseInventoryReport/getCaseInventoryReportInteractor.test.js index 421f511869e..db38144a438 100644 --- a/shared/src/business/useCases/caseInventoryReport/getCaseInventoryReportInteractor.test.js +++ b/shared/src/business/useCases/caseInventoryReport/getCaseInventoryReportInteractor.test.js @@ -4,19 +4,19 @@ const { const { getCaseInventoryReportInteractor, } = require('./getCaseInventoryReportInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getCaseInventoryReportInteractor', () => { beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '9754a349-1013-44fa-9e61-d39aba2637e0', }); }); it('throws an error if user is not authorized for case inventory report', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, //petitioner does not have CASE_INVENTORY_REPORT permission + role: ROLES.petitioner, //petitioner does not have CASE_INVENTORY_REPORT permission userId: '8e20dd1b-d142-40f4-8362-6297f1be68bf', }); diff --git a/shared/src/business/useCases/caseNote/deleteCaseNoteInteractor.test.js b/shared/src/business/useCases/caseNote/deleteCaseNoteInteractor.test.js index 9f1e4cdc207..8c91f2936dd 100644 --- a/shared/src/business/useCases/caseNote/deleteCaseNoteInteractor.test.js +++ b/shared/src/business/useCases/caseNote/deleteCaseNoteInteractor.test.js @@ -3,6 +3,7 @@ const { } = require('../../test/createTestApplicationContext'); const { deleteCaseNoteInteractor } = require('./deleteCaseNoteInteractor'); const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -26,7 +27,7 @@ describe('deleteCaseNoteInteractor', () => { it('deletes a procedural note', async () => { const mockUser = new User({ name: 'Judge Armen', - role: User.ROLES.judge, + role: ROLES.judge, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); diff --git a/shared/src/business/useCases/caseNote/deleteUserCaseNoteInteractor.test.js b/shared/src/business/useCases/caseNote/deleteUserCaseNoteInteractor.test.js index 8913b26b10c..91cb0a88fc4 100644 --- a/shared/src/business/useCases/caseNote/deleteUserCaseNoteInteractor.test.js +++ b/shared/src/business/useCases/caseNote/deleteUserCaseNoteInteractor.test.js @@ -4,6 +4,7 @@ const { const { deleteUserCaseNoteInteractor, } = require('./deleteUserCaseNoteInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -28,7 +29,7 @@ describe('deleteUserCaseNoteInteractor', () => { it('deletes a case note', async () => { const mockUser = new User({ name: 'Judge Armen', - role: User.ROLES.judge, + role: ROLES.judge, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -37,7 +38,7 @@ describe('deleteUserCaseNoteInteractor', () => { applicationContext.getUseCases.mockReturnValue({ getJudgeForUserChambersInteractor: () => ({ - role: User.ROLES.judge, + role: ROLES.judge, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), }); diff --git a/shared/src/business/useCases/caseNote/getUserCaseNoteForCasesInteractor.test.js b/shared/src/business/useCases/caseNote/getUserCaseNoteForCasesInteractor.test.js index 1224d270a66..60bc8c4ad10 100644 --- a/shared/src/business/useCases/caseNote/getUserCaseNoteForCasesInteractor.test.js +++ b/shared/src/business/useCases/caseNote/getUserCaseNoteForCasesInteractor.test.js @@ -5,8 +5,8 @@ const { getUserCaseNoteForCasesInteractor, } = require('./getUserCaseNoteForCasesInteractor'); const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const MOCK_NOTE = { caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -15,7 +15,7 @@ const MOCK_NOTE = { }; const mockJudge = { - role: User.ROLES.judge, + role: ROLES.judge, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }; diff --git a/shared/src/business/useCases/caseNote/getUserCaseNoteInteractor.test.js b/shared/src/business/useCases/caseNote/getUserCaseNoteInteractor.test.js index d0cb6cde476..5170b52b4ed 100644 --- a/shared/src/business/useCases/caseNote/getUserCaseNoteInteractor.test.js +++ b/shared/src/business/useCases/caseNote/getUserCaseNoteInteractor.test.js @@ -3,8 +3,8 @@ const { } = require('../../test/createTestApplicationContext'); const { getUserCaseNoteInteractor } = require('./getUserCaseNoteInteractor'); const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const MOCK_NOTE = { caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -18,7 +18,7 @@ const mockUnauthorizedUser = { }; const mockJudge = { - role: User.ROLES.judge, + role: ROLES.judge, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }; diff --git a/shared/src/business/useCases/caseNote/saveCaseNoteInteractor.test.js b/shared/src/business/useCases/caseNote/saveCaseNoteInteractor.test.js index ddf33b2ee14..ddfbac5ff1f 100644 --- a/shared/src/business/useCases/caseNote/saveCaseNoteInteractor.test.js +++ b/shared/src/business/useCases/caseNote/saveCaseNoteInteractor.test.js @@ -2,6 +2,7 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); const { saveCaseNoteInteractor } = require('./saveCaseNoteInteractor'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -26,7 +27,7 @@ describe('saveCaseNoteInteractor', () => { it('saves a case note', async () => { const mockJudge = new User({ name: 'Judge Armen', - role: User.ROLES.judge, + role: ROLES.judge, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); diff --git a/shared/src/business/useCases/caseNote/updateUserCaseNoteInteractor.test.js b/shared/src/business/useCases/caseNote/updateUserCaseNoteInteractor.test.js index e8e25998f73..1d9d9f1c9e9 100644 --- a/shared/src/business/useCases/caseNote/updateUserCaseNoteInteractor.test.js +++ b/shared/src/business/useCases/caseNote/updateUserCaseNoteInteractor.test.js @@ -4,6 +4,7 @@ const { const { updateUserCaseNoteInteractor, } = require('./updateUserCaseNoteInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -34,7 +35,7 @@ describe('updateUserCaseNoteInteractor', () => { it('updates a case note', async () => { const mockUser = new User({ name: 'Judge Armen', - role: User.ROLES.judge, + role: ROLES.judge, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); applicationContext.getCurrentUser.mockReturnValue(mockUser); @@ -42,7 +43,7 @@ describe('updateUserCaseNoteInteractor', () => { v.caseNoteToUpdate; applicationContext.getUseCases.mockReturnValue({ getJudgeForUserChambersInteractor: () => ({ - role: User.ROLES.judge, + role: ROLES.judge, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), }); diff --git a/shared/src/business/useCases/caseStatistics/addDeficiencyStatisticInteractor.js b/shared/src/business/useCases/caseStatistics/addDeficiencyStatisticInteractor.js new file mode 100644 index 00000000000..67d51087880 --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/addDeficiencyStatisticInteractor.js @@ -0,0 +1,69 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { Case } = require('../../entities/cases/Case'); +const { Statistic } = require('../../entities/Statistic'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * addDeficiencyStatisticInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to update statistics + * @param {number} providers.determinationDeficiencyAmount deficiency amount determined by the court + * @param {number} providers.determinationTotalPenalties total penalties amount determined by the court + * @param {number} providers.irsDeficiencyAmount deficiency amount from the IRS + * @param {number} providers.irsTotalPenalties total penalties amount from the IRS + * @param {string} providers.lastDateOfPeriod last date of the period for the statistic + * @param {number} providers.year year for the statistic + * @param {string} providers.yearOrPeriod whether the statistic is for a year or period + * @returns {object} the updated case + */ +exports.addDeficiencyStatisticInteractor = async ({ + applicationContext, + caseId, + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + year, + yearOrPeriod, +}) => { + const user = applicationContext.getCurrentUser(); + + if (!isAuthorized(user, ROLE_PERMISSIONS.ADD_EDIT_STATISTICS)) { + throw new UnauthorizedError('Unauthorized for editing statistics'); + } + + const oldCase = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ applicationContext, caseId }); + + const statisticEntity = new Statistic( + { + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + year, + yearOrPeriod, + }, + { applicationContext }, + ).validate(); + + const newCase = new Case(oldCase, { applicationContext }); + newCase.addStatistic(statisticEntity); + + const updatedCase = await applicationContext + .getPersistenceGateway() + .updateCase({ + applicationContext, + caseToUpdate: newCase.validate().toRawObject(), + }); + + return new Case(updatedCase, { applicationContext }).validate().toRawObject(); +}; diff --git a/shared/src/business/useCases/caseStatistics/addDeficiencyStatisticInteractor.test.js b/shared/src/business/useCases/caseStatistics/addDeficiencyStatisticInteractor.test.js new file mode 100644 index 00000000000..792798d0a12 --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/addDeficiencyStatisticInteractor.test.js @@ -0,0 +1,52 @@ +const { + addDeficiencyStatisticInteractor, +} = require('./addDeficiencyStatisticInteractor'); +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('addDeficiencyStatisticInteractor', () => { + beforeEach(() => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.docketClerk, + userId: 'docketClerk', + }); + + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(Promise.resolve(MOCK_CASE)); + }); + + it('should throw an error if the user is unauthorized to update case statistics', async () => { + applicationContext.getCurrentUser.mockReturnValue({}); + + await expect( + addDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + }), + ).rejects.toThrow('Unauthorized for editing statistics'); + }); + + it('should call updateCase with the updated case statistics and return the updated case', async () => { + const statistic = { + determinationDeficiencyAmount: 123, + determinationTotalPenalties: 456, + irsDeficiencyAmount: 789, + irsTotalPenalties: 1.1, + year: 2012, + yearOrPeriod: 'Year', + }; + + const result = await addDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + ...statistic, + }); + expect(result).toMatchObject({ + statistics: [statistic], + }); + }); +}); diff --git a/shared/src/business/useCases/caseStatistics/deleteDeficiencyStatisticInteractor.js b/shared/src/business/useCases/caseStatistics/deleteDeficiencyStatisticInteractor.js new file mode 100644 index 00000000000..b8aa38ccf82 --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/deleteDeficiencyStatisticInteractor.js @@ -0,0 +1,43 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { Case } = require('../../entities/cases/Case'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * deleteDeficiencyStatisticInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to delete statistics + * @param {string} providers.statisticId id of the statistic on the case to delete + * @returns {object} the updated case + */ +exports.deleteDeficiencyStatisticInteractor = async ({ + applicationContext, + caseId, + statisticId, +}) => { + const user = applicationContext.getCurrentUser(); + + if (!isAuthorized(user, ROLE_PERMISSIONS.ADD_EDIT_STATISTICS)) { + throw new UnauthorizedError('Unauthorized for editing statistics'); + } + + const oldCase = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ applicationContext, caseId }); + + const newCase = new Case(oldCase, { applicationContext }); + newCase.deleteStatistic(statisticId); + + const updatedCase = await applicationContext + .getPersistenceGateway() + .updateCase({ + applicationContext, + caseToUpdate: newCase.validate().toRawObject(), + }); + + return new Case(updatedCase, { applicationContext }).validate().toRawObject(); +}; diff --git a/shared/src/business/useCases/caseStatistics/deleteDeficiencyStatisticInteractor.test.js b/shared/src/business/useCases/caseStatistics/deleteDeficiencyStatisticInteractor.test.js new file mode 100644 index 00000000000..b2cb39dc98a --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/deleteDeficiencyStatisticInteractor.test.js @@ -0,0 +1,101 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + deleteDeficiencyStatisticInteractor, +} = require('./deleteDeficiencyStatisticInteractor'); +const { CASE_TYPES_MAP } = require('../../entities/EntityConstants'); +const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('deleteDeficiencyStatisticInteractor', () => { + const statisticId = 'f7a1cdb5-f534-4d12-a046-86ca3b46ddc4'; + + const statistic = { + determinationDeficiencyAmount: 123, + determinationTotalPenalties: 456, + irsDeficiencyAmount: 789, + irsTotalPenalties: 1.1, + statisticId, + year: 2012, + yearOrPeriod: 'Year', + }; + + beforeEach(() => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.docketClerk, + userId: 'docketClerk', + }); + + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue( + Promise.resolve({ ...MOCK_CASE, statistics: [statistic] }), + ); + }); + + it('should throw an error if the user is unauthorized to update case statistics', async () => { + applicationContext.getCurrentUser.mockReturnValue({}); + + await expect( + deleteDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + }), + ).rejects.toThrow('Unauthorized for editing statistics'); + }); + + it('should call updateCase with the removed case statistics and return the updated case', async () => { + const result = await deleteDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + statisticId, + }); + expect(result).toMatchObject({ + statistics: [], + }); + expect(applicationContext.getPersistenceGateway().updateCase).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().updateCase.mock.calls[0][0] + .caseToUpdate, + ).toMatchObject({ statistics: [] }); + }); + + it('should call updateCase with the original case statistics and return the original case if statisticId is not present on the case', async () => { + const result = await deleteDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + statisticId: '8b864301-a0d9-43aa-8029-e1a0ed8ad4c9', + }); + expect(result).toMatchObject({ + statistics: [statistic], + }); + expect(applicationContext.getPersistenceGateway().updateCase).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().updateCase.mock.calls[0][0] + .caseToUpdate, + ).toMatchObject({ statistics: [statistic] }); + }); + + it('should throw an error and not update the case if attempting to delete the only statistic from a deficiency case with hasVerifiedIrsNotice true (at least one statistic is required)', async () => { + applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue( + Promise.resolve({ + ...MOCK_CASE, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: true, + statistics: [statistic], + }), + ); + + await expect( + deleteDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + statisticId: statistic.statisticId, + }), + ).rejects.toThrow('The Case entity was invalid'); + expect( + applicationContext.getPersistenceGateway().updateCase, + ).not.toBeCalled(); + }); +}); diff --git a/shared/src/business/useCases/caseStatistics/updateDeficiencyStatisticInteractor.js b/shared/src/business/useCases/caseStatistics/updateDeficiencyStatisticInteractor.js new file mode 100644 index 00000000000..23d8dbdf8de --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/updateDeficiencyStatisticInteractor.js @@ -0,0 +1,72 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { Case } = require('../../entities/cases/Case'); +const { Statistic } = require('../../entities/Statistic'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * updateDeficiencyStatisticInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to update statistics + * @param {number} providers.determinationDeficiencyAmount deficiency amount determined by the court + * @param {number} providers.determinationTotalPenalties total penalties amount determined by the court + * @param {number} providers.irsDeficiencyAmount deficiency amount from the IRS + * @param {number} providers.irsTotalPenalties total penalties amount from the IRS + * @param {string} providers.lastDateOfPeriod last date of the period for the statistic + * @param {string} providers.statisticId id of the statistic on the case to update + * @param {number} providers.year year for the statistic + * @param {string} providers.yearOrPeriod whether the statistic is for a year or period + * @returns {object} the updated case + */ +exports.updateDeficiencyStatisticInteractor = async ({ + applicationContext, + caseId, + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + statisticId, + year, + yearOrPeriod, +}) => { + const user = applicationContext.getCurrentUser(); + + if (!isAuthorized(user, ROLE_PERMISSIONS.ADD_EDIT_STATISTICS)) { + throw new UnauthorizedError('Unauthorized for editing statistics'); + } + + const oldCase = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ applicationContext, caseId }); + + const statisticEntity = new Statistic( + { + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + statisticId, + year, + yearOrPeriod, + }, + { applicationContext }, + ).validate(); + + const newCase = new Case(oldCase, { applicationContext }); + newCase.updateStatistic(statisticEntity, statisticId); + + const updatedCase = await applicationContext + .getPersistenceGateway() + .updateCase({ + applicationContext, + caseToUpdate: newCase.validate().toRawObject(), + }); + + return new Case(updatedCase, { applicationContext }).validate().toRawObject(); +}; diff --git a/shared/src/business/useCases/caseStatistics/updateDeficiencyStatisticInteractor.test.js b/shared/src/business/useCases/caseStatistics/updateDeficiencyStatisticInteractor.test.js new file mode 100644 index 00000000000..848ef957825 --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/updateDeficiencyStatisticInteractor.test.js @@ -0,0 +1,79 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + updateDeficiencyStatisticInteractor, +} = require('./updateDeficiencyStatisticInteractor'); +const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('updateDeficiencyStatisticInteractor', () => { + let statistic = { + determinationDeficiencyAmount: 123, + determinationTotalPenalties: 456, + irsDeficiencyAmount: 789, + irsTotalPenalties: 1.1, + statisticId: '7452b87f-7ba3-45c7-ae4b-bd1eab37c866', + year: 2012, + yearOrPeriod: 'Year', + }; + + beforeEach(() => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.docketClerk, + userId: 'docketClerk', + }); + + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue( + Promise.resolve({ ...MOCK_CASE, statistics: [statistic] }), + ); + }); + + it('should throw an error if the user is unauthorized to update case statistics', async () => { + applicationContext.getCurrentUser.mockReturnValue({}); + + await expect( + updateDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + }), + ).rejects.toThrow('Unauthorized for editing statistics'); + }); + + it('should call updateCase with the updated case statistics and return the updated case', async () => { + const statisticToUpdate = { + ...statistic, + determinationDeficiencyAmount: 1, + }; + + const result = await updateDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + statisticId: '7452b87f-7ba3-45c7-ae4b-bd1eab37c866', + ...statisticToUpdate, + }); + expect(result).toMatchObject({ + statistics: [statisticToUpdate], + }); + }); + + it('should call updateCase with the original case statistics and return the original case if statisticId is not present on the case', async () => { + const statisticToUpdate = { + ...statistic, + determinationDeficiencyAmount: 1, + statisticId: 'a3f2aa54-ad95-4396-b1a9-2d90d9e22242', + }; + + const result = await updateDeficiencyStatisticInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + statisticId: 'a3f2aa54-ad95-4396-b1a9-2d90d9e22242', + ...statisticToUpdate, + }); + expect(result).toMatchObject({ + statistics: [statistic], + }); + }); +}); diff --git a/shared/src/business/useCases/caseStatistics/updateOtherStatisticsInteractor.js b/shared/src/business/useCases/caseStatistics/updateOtherStatisticsInteractor.js new file mode 100644 index 00000000000..1431d84b557 --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/updateOtherStatisticsInteractor.js @@ -0,0 +1,47 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { Case } = require('../../entities/cases/Case'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * updateOtherStatisticsInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to update statistics + * @param {number} providers.damages damages statistic to add to the case + * @param {number} providers.litigationCosts litigation costs statistic to add to the case + * @returns {object} the updated case + */ +exports.updateOtherStatisticsInteractor = async ({ + applicationContext, + caseId, + damages, + litigationCosts, +}) => { + const user = applicationContext.getCurrentUser(); + + if (!isAuthorized(user, ROLE_PERMISSIONS.ADD_EDIT_STATISTICS)) { + throw new UnauthorizedError('Unauthorized for editing statistics'); + } + + const oldCase = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ applicationContext, caseId }); + + const newCase = new Case( + { ...oldCase, damages, litigationCosts }, + { applicationContext }, + ); + + const updatedCase = await applicationContext + .getPersistenceGateway() + .updateCase({ + applicationContext, + caseToUpdate: newCase.validate().toRawObject(), + }); + + return new Case(updatedCase, { applicationContext }).validate().toRawObject(); +}; diff --git a/shared/src/business/useCases/caseStatistics/updateOtherStatisticsInteractor.test.js b/shared/src/business/useCases/caseStatistics/updateOtherStatisticsInteractor.test.js new file mode 100644 index 00000000000..d1826359db4 --- /dev/null +++ b/shared/src/business/useCases/caseStatistics/updateOtherStatisticsInteractor.test.js @@ -0,0 +1,45 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + updateOtherStatisticsInteractor, +} = require('./updateOtherStatisticsInteractor'); +const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('updateOtherStatisticsInteractor', () => { + beforeEach(() => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.docketClerk, + userId: 'docketClerk', + }); + + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(Promise.resolve(MOCK_CASE)); + }); + + it('should throw an error if the user is unauthorized to update case statistics', async () => { + applicationContext.getCurrentUser.mockReturnValue({}); + + await expect( + updateOtherStatisticsInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + }), + ).rejects.toThrow('Unauthorized for editing statistics'); + }); + + it('should call updateCase with the updated case statistics and return the updated case', async () => { + const result = await updateOtherStatisticsInteractor({ + applicationContext, + caseId: MOCK_CASE.caseId, + damages: 1234, + litigationCosts: 5678, + }); + expect(result).toMatchObject({ + damages: 1234, + litigationCosts: 5678, + }); + }); +}); diff --git a/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.js b/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.js index f5e9a03c645..ec4921e4650 100644 --- a/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.js +++ b/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.js @@ -1,4 +1,5 @@ const { Case } = require('../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { createISODateString } = require('../utilities/DateHandler'); /** @@ -47,11 +48,11 @@ exports.checkForReadyForTrialCasesInteractor = async ({ if (caseToCheck) { const caseEntity = new Case(caseToCheck, { applicationContext }); - if (caseEntity.status === Case.STATUS_TYPES.generalDocket) { + if (caseEntity.status === CASE_STATUS_TYPES.generalDocket) { caseEntity.checkForReadyForTrial(); if ( - caseEntity.status === Case.STATUS_TYPES.generalDocketReadyForTrial + caseEntity.status === CASE_STATUS_TYPES.generalDocketReadyForTrial ) { updatedCases.push(updateForTrial(caseEntity)); } diff --git a/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.test.js b/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.test.js index d52c439c3ac..b1e194011f1 100644 --- a/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.test.js +++ b/shared/src/business/useCases/checkForReadyForTrialCasesInteractor.test.js @@ -2,7 +2,7 @@ const { checkForReadyForTrialCasesInteractor, } = require('./checkForReadyForTrialCasesInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { MOCK_CASE } = require('../../test/mockCase'); const { MOCK_USERS } = require('../../test/mockUsers'); @@ -91,7 +91,7 @@ describe('checkForReadyForTrialCasesInteractor', () => { workItems: [], }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }); applicationContext.getPersistenceGateway().updateCase.mockReturnValue({}); @@ -118,7 +118,7 @@ describe('checkForReadyForTrialCasesInteractor', () => { */ applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ ...MOCK_CASE, - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }); applicationContext.getPersistenceGateway().updateCase.mockReturnValue({}); diff --git a/shared/src/business/useCases/correspondence/deleteCorrespondenceDocumentInteractor.js b/shared/src/business/useCases/correspondence/deleteCorrespondenceDocumentInteractor.js new file mode 100644 index 00000000000..0d56ea3c331 --- /dev/null +++ b/shared/src/business/useCases/correspondence/deleteCorrespondenceDocumentInteractor.js @@ -0,0 +1,28 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { UnauthorizedError } = require('../../../errors/errors'); + +exports.deleteCorrespondenceDocumentInteractor = async ({ + applicationContext, + caseId, + documentId, +}) => { + const user = applicationContext.getCurrentUser(); + + if (!isAuthorized(user, ROLE_PERMISSIONS.CASE_CORRESPONDENCE)) { + throw new UnauthorizedError('Unauthorized'); + } + + await applicationContext.getPersistenceGateway().deleteDocument({ + applicationContext, + key: documentId, + }); + + await applicationContext.getPersistenceGateway().deleteCaseCorrespondence({ + applicationContext, + caseId, + documentId, + }); +}; diff --git a/shared/src/business/useCases/correspondence/deleteCorrespondenceDocumentInteractor.test.js b/shared/src/business/useCases/correspondence/deleteCorrespondenceDocumentInteractor.test.js new file mode 100644 index 00000000000..d6a63046623 --- /dev/null +++ b/shared/src/business/useCases/correspondence/deleteCorrespondenceDocumentInteractor.test.js @@ -0,0 +1,62 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + deleteCorrespondenceDocumentInteractor, +} = require('./deleteCorrespondenceDocumentInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('deleteCorrespondenceDocumentInteractor', () => { + let mockUser; + + beforeEach(() => { + mockUser = { + name: 'Docket Clerk', + role: ROLES.docketClerk, + userId: '2474e5c0-f741-4120-befa-b77378ac8bf0', + }; + applicationContext.getCurrentUser.mockImplementation(() => mockUser); + }); + + it('should throw an Unauthorized error if the user role does not have the CASE_CORRESPONDENCE permission', async () => { + const user = { ...mockUser, role: ROLES.petitioner }; + applicationContext.getCurrentUser.mockReturnValue(user); + + await expect( + deleteCorrespondenceDocumentInteractor({ + applicationContext, + }), + ).rejects.toThrow('Unauthorized'); + }); + + it('should delete the specified correspondence document from s3', async () => { + await deleteCorrespondenceDocumentInteractor({ + applicationContext, + caseId: '111', + documentId: 'abc', + }); + + expect( + applicationContext.getPersistenceGateway().deleteDocument.mock + .calls[0][0], + ).toMatchObject({ + key: 'abc', + }); + }); + + it('should delete the specified correspondence document from the case', async () => { + await deleteCorrespondenceDocumentInteractor({ + applicationContext, + caseId: '111', + documentId: 'abc', + }); + + expect( + applicationContext.getPersistenceGateway().deleteCaseCorrespondence.mock + .calls[0][0], + ).toMatchObject({ + caseId: '111', + documentId: 'abc', + }); + }); +}); diff --git a/shared/src/business/useCases/correspondence/fileCorrespondenceDocumentInteractor.test.js b/shared/src/business/useCases/correspondence/fileCorrespondenceDocumentInteractor.test.js index 95cff07c3c4..bc67ec18854 100644 --- a/shared/src/business/useCases/correspondence/fileCorrespondenceDocumentInteractor.test.js +++ b/shared/src/business/useCases/correspondence/fileCorrespondenceDocumentInteractor.test.js @@ -1,19 +1,22 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { + COUNTRY_TYPES, + PARTY_TYPES, + ROLES, +} = require('../../entities/EntityConstants'); const { fileCorrespondenceDocumentInteractor, } = require('./fileCorrespondenceDocumentInteractor'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); const { createISODateString } = require('../../utilities/DateHandler'); -const { User } = require('../../entities/User'); describe('fileCorrespondenceDocumentInteractor', () => { const mockDocumentId = 'cf105788-5d34-4451-aa8d-dfd9a851b675'; const mockUser = { name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '2474e5c0-f741-4120-befa-b77378ac8bf0', }; const mockCase = { @@ -23,7 +26,7 @@ describe('fileCorrespondenceDocumentInteractor', () => { contactPrimary: { address1: '123 Main St', city: 'Somewhere', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, email: 'contact@example.com', name: 'Contact Primary', phone: '123123134', @@ -65,7 +68,7 @@ describe('fileCorrespondenceDocumentInteractor', () => { }, ], filingType: 'Myself', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', }; @@ -82,14 +85,14 @@ describe('fileCorrespondenceDocumentInteractor', () => { }); it('should throw an Unauthorized error if the user role does not have theCASE_CORRESPONDENCE permission', async () => { - const user = { ...mockUser, role: User.ROLES.petitioner }; + const user = { ...mockUser, role: ROLES.petitioner }; applicationContext.getCurrentUser.mockReturnValue(user); await expect( fileCorrespondenceDocumentInteractor({ applicationContext, - documentMetadata: { caseId: '123' }, - primaryDocumentFileId: '111', + documentMetadata: { caseId: '2cb1e611-df1c-4c15-bfc2-491248551672' }, + primaryDocumentFileId: '14bb669b-0962-4781-87a0-50718f556e2b', }), ).rejects.toThrow('Unauthorized'); }); @@ -102,10 +105,12 @@ describe('fileCorrespondenceDocumentInteractor', () => { await expect( fileCorrespondenceDocumentInteractor({ applicationContext, - documentMetadata: { caseId: '123' }, - primaryDocumentFileId: '111', + documentMetadata: { caseId: '2cb1e611-df1c-4c15-bfc2-491248551672' }, + primaryDocumentFileId: '14bb669b-0962-4781-87a0-50718f556e2b', }), - ).rejects.toThrow('Case 123 was not found'); + ).rejects.toThrow( + 'Case 2cb1e611-df1c-4c15-bfc2-491248551672 was not found', + ); }); it('should add the correspondence document to the case when the case entity is valid', async () => { @@ -120,19 +125,19 @@ describe('fileCorrespondenceDocumentInteractor', () => { await fileCorrespondenceDocumentInteractor({ applicationContext, documentMetadata: { - caseId: '123', + caseId: '2cb1e611-df1c-4c15-bfc2-491248551672', documentTitle: 'A title', filingDate: '2001-02-01', }, - primaryDocumentFileId: '111', + primaryDocumentFileId: '14bb669b-0962-4781-87a0-50718f556e2b', }); expect( applicationContext.getPersistenceGateway().fileCaseCorrespondence.mock .calls[0][0], ).toMatchObject({ - caseId: '123', + caseId: '2cb1e611-df1c-4c15-bfc2-491248551672', correspondence: { - documentId: '111', + documentId: '14bb669b-0962-4781-87a0-50718f556e2b', documentTitle: 'A title', filedBy: mockUser.name, filingDate: '2001-02-01', @@ -149,17 +154,17 @@ describe('fileCorrespondenceDocumentInteractor', () => { const result = await fileCorrespondenceDocumentInteractor({ applicationContext, documentMetadata: { - caseId: '123', + caseId: '2cb1e611-df1c-4c15-bfc2-491248551672', documentTitle: 'A title', filingDate: '2001-02-01', }, - primaryDocumentFileId: '111', + primaryDocumentFileId: '14bb669b-0962-4781-87a0-50718f556e2b', }); expect(result).toMatchObject({ ...mockCase, correspondence: [ { - documentId: '111', + documentId: '14bb669b-0962-4781-87a0-50718f556e2b', documentTitle: 'A title', filedBy: mockUser.name, filingDate: '2001-02-01', diff --git a/shared/src/business/useCases/correspondence/updateCorrespondenceDocumentInteractor.js b/shared/src/business/useCases/correspondence/updateCorrespondenceDocumentInteractor.js new file mode 100644 index 00000000000..5d71baae5d4 --- /dev/null +++ b/shared/src/business/useCases/correspondence/updateCorrespondenceDocumentInteractor.js @@ -0,0 +1,59 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { Case } = require('../../entities/cases/Case'); +const { Correspondence } = require('../../entities/Correspondence'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {object} providers.documentMetadata the document metadata + * @returns {Promise<*>} the updated case entity after the correspondence document is updated + */ +exports.updateCorrespondenceDocumentInteractor = async ({ + applicationContext, + documentMetadata, +}) => { + const authorizedUser = applicationContext.getCurrentUser(); + const { caseId } = documentMetadata; + + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.CASE_CORRESPONDENCE)) { + throw new UnauthorizedError('Unauthorized'); + } + + const caseToUpdate = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ + applicationContext, + caseId, + }); + + const caseEntity = new Case(caseToUpdate, { applicationContext }); + + const currentCorrespondenceDocument = caseEntity.getDocumentById({ + documentId: documentMetadata.documentId, + }); + + const updatedCorrespondenceEntity = new Correspondence( + { + ...currentCorrespondenceDocument, + documentTitle: documentMetadata.documentTitle, + }, + { applicationContext }, + ); + + caseEntity.updateDocument(updatedCorrespondenceEntity); + + const caseEntityRaw = caseEntity.validate().toRawObject(); + + await applicationContext.getPersistenceGateway().fileCaseCorrespondence({ + applicationContext, + caseId, + correspondence: updatedCorrespondenceEntity.validate().toRawObject(), + }); + + return caseEntityRaw; +}; diff --git a/shared/src/business/useCases/correspondence/updateCorrespondenceDocumentInteractor.test.js b/shared/src/business/useCases/correspondence/updateCorrespondenceDocumentInteractor.test.js new file mode 100644 index 00000000000..86c519b4585 --- /dev/null +++ b/shared/src/business/useCases/correspondence/updateCorrespondenceDocumentInteractor.test.js @@ -0,0 +1,146 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + COUNTRY_TYPES, + PARTY_TYPES, + ROLES, +} = require('../../entities/EntityConstants'); +const { + updateCorrespondenceDocumentInteractor, +} = require('./updateCorrespondenceDocumentInteractor'); +const { Correspondence } = require('../../entities/Correspondence'); +const { createISODateString } = require('../../utilities/DateHandler'); + +describe('updateCorrespondenceDocumentInteractor', () => { + let mockUser; + const mockDocumentId = 'cf105788-5d34-4451-aa8d-dfd9a851b675'; + const mockUserFixture = { + name: 'Docket Clerk', + role: ROLES.docketClerk, + userId: '2474e5c0-f741-4120-befa-b77378ac8bf0', + }; + const mockCorrespondence = new Correspondence({ + documentId: '74e36bf7-dcbd-4ee7-a9ec-6d7446096df8', + documentTitle: 'old document title', + filedBy: 'docket clerk', + userId: '5980d666-641d-455a-8386-18908d50c98e', + }); + const mockCase = { + caseCaption: 'Caption', + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + caseType: 'Deficiency', + contactPrimary: { + address1: '123 Main St', + city: 'Somewhere', + countryType: COUNTRY_TYPES.DOMESTIC, + email: 'contact@example.com', + name: 'Contact Primary', + phone: '123123134', + postalCode: '12345', + state: 'TN', + }, + correspondence: [mockCorrespondence], + docketNumber: '123-45', + docketRecord: [ + { + description: 'Docket Record 0', + docketRecordId: 'c54ba5a9-b37b-479d-9201-067ec6e335bc', + documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bc', + eventCode: 'O', + filingDate: createISODateString(), + index: 0, + }, + { + description: 'Docket Record 1', + docketRecordId: mockDocumentId, + documentId: mockDocumentId, + eventCode: 'OAJ', + filingDate: createISODateString(), + index: 1, + }, + ], + documents: [ + { + documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bc', + documentType: 'O - Order', + eventCode: 'O', + serviceStamp: 'Served', + userId: '2474e5c0-f741-4120-befa-b77378ac8bf0', + }, + { + documentId: mockDocumentId, + documentType: 'OAJ - Order that case is assigned', + eventCode: 'OAJ', + userId: '2474e5c0-f741-4120-befa-b77378ac8bf0', + }, + ], + filingType: 'Myself', + partyType: PARTY_TYPES.petitioner, + preferredTrialCity: 'Fresno, California', + procedureType: 'Regular', + }; + + beforeEach(() => { + mockUser = mockUserFixture; + + applicationContext.getCurrentUser.mockImplementation(() => mockUser); + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(mockCase); + }); + + it('should throw an Unauthorized error if the user role does not have the CASE_CORRESPONDENCE permission', async () => { + mockUser = { ...mockUser, role: ROLES.petitioner }; + + await expect( + updateCorrespondenceDocumentInteractor({ + applicationContext, + documentMetadata: { caseId: '2368f6c6-e91f-4df3-98fd-43e55c00f6f1' }, + }), + ).rejects.toThrow('Unauthorized'); + }); + + it('should update the specified correspondence document title when the case entity is valid', async () => { + await updateCorrespondenceDocumentInteractor({ + applicationContext, + documentMetadata: { + caseId: '2e528c9c-70f5-4b3e-81c6-1a2f715261b4', + documentId: mockCorrespondence.documentId, + documentTitle: 'A title that has been updated', + }, + }); + + expect( + applicationContext.getPersistenceGateway().fileCaseCorrespondence.mock + .calls[0][0], + ).toMatchObject({ + caseId: '2e528c9c-70f5-4b3e-81c6-1a2f715261b4', + correspondence: { + ...mockCorrespondence, + documentTitle: 'A title that has been updated', + }, + }); + }); + + it('should return an updated raw case object', async () => { + const result = await updateCorrespondenceDocumentInteractor({ + applicationContext, + documentMetadata: { + caseId: '2368f6c6-e91f-4df3-98fd-43e55c00f6f1', + documentId: mockCorrespondence.documentId, + documentTitle: 'A title that has been updated', + }, + }); + + expect(result).toMatchObject({ + ...mockCase, + correspondence: [ + { + ...mockCorrespondence, + documentTitle: 'A title that has been updated', + }, + ], + }); + }); +}); diff --git a/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.js b/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.js index 40609c4d147..ca8a8af0e43 100644 --- a/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.js +++ b/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.js @@ -1,5 +1,4 @@ const { getPageDimensions } = require('../generateSignedDocumentInteractor'); -const { PDFDocument, rgb, StandardFonts } = require('pdf-lib'); /** * addServedStampToDocument @@ -9,7 +8,17 @@ const { PDFDocument, rgb, StandardFonts } = require('pdf-lib'); * @param {string} providers.serviceStampText the service stamp text to add to the document * @returns {object} the new pdf with the stamp at the bottom center of the document */ -exports.addServedStampToDocument = async ({ pdfData, serviceStampText }) => { +exports.addServedStampToDocument = async ({ + applicationContext, + pdfData, + serviceStampText, +}) => { + const { + PDFDocument, + rgb, + StandardFonts, + } = await applicationContext.getPdfLib(); + const scale = 1; const pdfDoc = await PDFDocument.load(pdfData); const pages = pdfDoc.getPages(); diff --git a/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.test.js b/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.test.js index 679ead49a7d..2af3b179d11 100644 --- a/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.test.js +++ b/shared/src/business/useCases/courtIssuedDocument/addServedStampToDocument.test.js @@ -1,8 +1,10 @@ const fs = require('fs'); const path = require('path'); +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); const { addServedStampToDocument } = require('./addServedStampToDocument.js'); const { PDFDocument } = require('pdf-lib'); - const testAssetsPath = path.join(__dirname, '../../../../test-assets/'); const testPdfDocBytes = () => { @@ -19,6 +21,7 @@ describe('addServedStampToDocument', () => { it('adds a served stamp to a pdf document without changing the number of pages', async () => { const newPdfData = await addServedStampToDocument({ + applicationContext, pdfData: testPdfDoc, serviceStampText: 'Test', }); diff --git a/shared/src/business/useCases/courtIssuedDocument/generateCourtIssuedDocumentTitleInteractor.js b/shared/src/business/useCases/courtIssuedDocument/generateCourtIssuedDocumentTitleInteractor.js index 3b77c40eaea..d564e2e9d45 100644 --- a/shared/src/business/useCases/courtIssuedDocument/generateCourtIssuedDocumentTitleInteractor.js +++ b/shared/src/business/useCases/courtIssuedDocument/generateCourtIssuedDocumentTitleInteractor.js @@ -1,7 +1,7 @@ const { CourtIssuedDocumentFactory, } = require('../../entities/courtIssuedDocument/CourtIssuedDocumentFactory'); -const { Document } = require('../../entities/Document'); +const { COURT_ISSUED_EVENT_CODES } = require('../../entities/EntityConstants'); /** * generateCourtIssuedDocumentTitleInteractor @@ -11,7 +11,7 @@ const { Document } = require('../../entities/Document'); * @returns {string} document title */ exports.generateCourtIssuedDocumentTitleInteractor = ({ documentMetadata }) => { - const filingEvent = Document.COURT_ISSUED_EVENT_CODES.find( + const filingEvent = COURT_ISSUED_EVENT_CODES.find( document => documentMetadata.eventCode === document.eventCode, ); diff --git a/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.js b/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.js index bfb23a333d7..fb8acb85bd1 100644 --- a/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.js +++ b/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.js @@ -20,7 +20,6 @@ const { addServedStampToDocument } = require('./addServedStampToDocument'); const { Case } = require('../../entities/cases/Case'); const { DocketRecord } = require('../../entities/DocketRecord'); const { NotFoundError, UnauthorizedError } = require('../../../errors/errors'); -const { PDFDocument } = require('pdf-lib'); const { TrialSession } = require('../../entities/trialSessions/TrialSession'); const completeWorkItem = async ({ @@ -65,6 +64,8 @@ exports.serveCourtIssuedDocumentInteractor = async ({ }) => { const user = applicationContext.getCurrentUser(); + const { PDFDocument } = await applicationContext.getPdfLib(); + if (!isAuthorized(user, ROLE_PERMISSIONS.SERVE_DOCUMENT)) { throw new UnauthorizedError('Unauthorized for document service'); } @@ -128,6 +129,7 @@ exports.serveCourtIssuedDocumentInteractor = async ({ ); const newPdfData = await addServedStampToDocument({ + applicationContext, pdfData, serviceStampText: `${serviceStampType} ${serviceStampDate}`, }); @@ -202,7 +204,7 @@ exports.serveCourtIssuedDocumentInteractor = async ({ await applicationContext.getUseCaseHelpers().sendServedPartiesEmails({ applicationContext, - caseEntity: caseToUpdate, + caseEntity, documentEntity: courtIssuedDocument, servedParties, }); @@ -226,6 +228,7 @@ exports.serveCourtIssuedDocumentInteractor = async ({ const { url } = await saveFileAndGenerateUrl({ applicationContext, file: paperServicePdfData, + useTempBucket: true, }); return { pdfUrl: url }; diff --git a/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.test.js b/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.test.js index 435abbcc257..eceee7dc7e2 100644 --- a/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.test.js +++ b/shared/src/business/useCases/courtIssuedDocument/serveCourtIssuedDocumentInteractor.test.js @@ -3,18 +3,20 @@ const path = require('path'); const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { + CASE_STATUS_TYPES, + COUNTRY_TYPES, + COURT_ISSUED_EVENT_CODES, +} = require('../../entities/EntityConstants'); const { ENTERED_AND_SERVED_EVENT_CODES, } = require('../../entities/courtIssuedDocument/CourtIssuedDocumentConstants'); const { serveCourtIssuedDocumentInteractor, } = require('./serveCourtIssuedDocumentInteractor'); -const { Case } = require('../../entities/cases/Case'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); const { createISODateString } = require('../../utilities/DateHandler'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); -const { Document } = require('../../entities/Document'); -const { User } = require('../../entities/User'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); +const { PARTY_TYPES, ROLES } = require('../../entities/EntityConstants'); const { v4: uuidv4 } = require('uuid'); const testAssetsPath = path.join(__dirname, '../../../../test-assets/'); @@ -33,7 +35,8 @@ describe('serveCourtIssuedDocumentInteractor', () => { }; const mockUser = { - role: User.ROLES.docketClerk, + name: 'Docket Clerk', + role: ROLES.docketClerk, userId: '2474e5c0-f741-4120-befa-b77378ac8bf0', }; @@ -42,7 +45,8 @@ describe('serveCourtIssuedDocumentInteractor', () => { docketNumber: '123-45', isQC: true, section: DOCKET_SECTION, - sentBy: mockUser.userId, + sentBy: mockUser.name, + sentByUserId: mockUser.userId, workItemId: 'b4c7337f-9ca0-45d9-9396-75e003f81e32', }; @@ -63,7 +67,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { index, }); - const eventCodeMap = Document.COURT_ISSUED_EVENT_CODES.find( + const eventCodeMap = COURT_ISSUED_EVENT_CODES.find( entry => entry.eventCode === eventCode, ); @@ -85,7 +89,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { contactPrimary: { address1: '123 Main St', city: 'Somewhere', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, email: 'contact@example.com', name: 'Contact Primary', phone: '123123134', @@ -131,7 +135,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { ...documentsWithCaseClosingEventCodes, ], filingType: 'Myself', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', }, @@ -142,7 +146,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { contactPrimary: { address1: '123 Main St', city: 'Somewhere', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, name: 'Contact Primary', phone: '123123134', postalCode: '12345', @@ -151,7 +155,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { contactSecondary: { address1: '123 Main St', city: 'Somewhere', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, name: 'Contact Secondary', phone: '123123134', postalCode: '12345', @@ -198,7 +202,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { filingType: 'Myself', isPaper: true, mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', }, @@ -226,9 +230,6 @@ describe('serveCourtIssuedDocumentInteractor', () => { applicationContext .getPersistenceGateway() .updateCase.mockImplementation(caseToUpdate => caseToUpdate); - applicationContext - .getUseCaseHelpers() - .generatePaperServiceAddressPagePdf.mockResolvedValue(testPdfDoc); applicationContext .getUseCaseHelpers() .countPagesInDocument.mockResolvedValue(1); @@ -272,7 +273,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { it('should throw an Unauthorized error if the user role does not have the SERVE_DOCUMENT permission', async () => { // petitioner role does NOT have the SERVE_DOCUMENT permission - const user = { ...mockUser, role: User.ROLES.petitioner }; + const user = { ...mockUser, role: ROLES.petitioner }; applicationContext.getCurrentUser.mockReturnValue(user); await expect( @@ -485,7 +486,7 @@ describe('serveCourtIssuedDocumentInteractor', () => { const updatedCase = applicationContext.getPersistenceGateway().updateCase .mock.calls[0][0].caseToUpdate; - expect(updatedCase.status).toEqual(Case.STATUS_TYPES.closed); + expect(updatedCase.status).toEqual(CASE_STATUS_TYPES.closed); expect( applicationContext.getPersistenceGateway() .deleteCaseTrialSortMappingRecords, diff --git a/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.js b/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.js index 9e4b313816f..f83701b9f65 100644 --- a/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.js +++ b/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.js @@ -1,77 +1,62 @@ -const { pdfStyles } = require('../../../tools/pdfStyles'); - +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { getCaseCaptionMeta } = require('../../utilities/getCaseCaptionMeta'); +const { UnauthorizedError } = require('../../../errors/errors'); /** * * createCourtIssuedOrderPdfFromHtmlInteractor * * @param {object} providers the providers object * @param {object} providers.applicationContext the application context - * @param {string} providers.docketNumberWithSuffix the docket number of the case with the suffix - * @param {string} providers.htmlString the htmlString for the pdf content - * @returns {Buffer} the pdf as a binary buffer + * @param {string} providers.caseId the case id where the order is generated + * @param {string} providers.contentHtml the html string for the pdf content + * @param {string} providers.documentTitle the title of the document + * @param {string} providers.signatureText (optional) text to be used as the signatory of the document + * @returns {string} url for the generated and stored document */ exports.createCourtIssuedOrderPdfFromHtmlInteractor = async ({ applicationContext, - docketNumberWithSuffix, - htmlString, + caseId, + contentHtml, + documentTitle, + signatureText, }) => { - let browser = null; - let result = null; - - try { - browser = await applicationContext.getChromiumBrowser(); - let page = await browser.newPage(); + const user = applicationContext.getCurrentUser(); - await page.setContent(htmlString); + if (!isAuthorized(user, ROLE_PERMISSIONS.COURT_ISSUED_DOCUMENT)) { + throw new UnauthorizedError('Unauthorized'); + } - const headerTemplate = ` - - - - - - -
-
- Page - of -
-
- Docket ${docketNumberWithSuffix} -
-
- - - `; + const caseDetail = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ + applicationContext, + caseId, + }); - const footerTemplate = ` - - - -
-
- - - `; + const { + caseCaptionExtension, + caseTitle, + docketNumberWithSuffix, + } = getCaseCaptionMeta(caseDetail); - result = await page.pdf({ - displayHeaderFooter: true, - footerTemplate, - format: 'letter', - headerTemplate, - }); - } catch (error) { - applicationContext.logger.error(error); - throw error; - } finally { - if (browser !== null) { - await browser.close(); - } - } + const orderPdf = await applicationContext.getDocumentGenerators().order({ + applicationContext, + data: { + caseCaptionExtension: caseCaptionExtension, + caseTitle: caseTitle, + docketNumberWithSuffix, + orderContent: contentHtml, + orderTitle: documentTitle, + signatureText, + }, + }); - return await applicationContext - .getUseCaseHelpers() - .saveFileAndGenerateUrl({ applicationContext, file: result }); + return await applicationContext.getUseCaseHelpers().saveFileAndGenerateUrl({ + applicationContext, + file: orderPdf, + useTempBucket: true, + }); }; diff --git a/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.test.js b/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.test.js index 4a4b2b565c6..fd66d07a03e 100644 --- a/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.test.js +++ b/shared/src/business/useCases/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlInteractor.test.js @@ -6,28 +6,63 @@ const { } = require('./createCourtIssuedOrderPdfFromHtmlInteractor'); describe('createCourtIssuedOrderPdfFromHtmlInteractor', () => { - it('returns the pdf url', async () => { - const mockPdfUrl = 'www.example.com'; + const mockPdfUrl = 'www.example.com'; + + beforeAll(() => { + applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ + caseCaption: 'Dr. Leo Marvin, Petitioner', + caseId: '123', + }); + applicationContext .getUseCaseHelpers() .saveFileAndGenerateUrl.mockReturnValue(mockPdfUrl); + }); - const result = await createCourtIssuedOrderPdfFromHtmlInteractor({ - applicationContext, + beforeEach(() => { + applicationContext.getCurrentUser.mockReturnValue({ + role: 'docketclerk', + userId: '321', }); - - expect(result).toEqual(mockPdfUrl); }); - it('should catch, log, and rethrow an error thrown by chromium', async () => { - applicationContext.getChromiumBrowser.mockImplementation(() => { - throw new Error('some chromium error'); + it('throws an error if the user is not authorized', async () => { + applicationContext.getCurrentUser.mockReturnValue({ + role: 'petitioner', + userId: '432', }); await expect( createCourtIssuedOrderPdfFromHtmlInteractor({ applicationContext, }), - ).rejects.toThrow('some chromium error'); + ).rejects.toThrow('Unauthorized'); + }); + + it('fetches the case by id', async () => { + await createCourtIssuedOrderPdfFromHtmlInteractor({ + applicationContext, + }); + expect( + applicationContext.getPersistenceGateway().getCaseByCaseId, + ).toHaveBeenCalled(); + }); + + it('calls the pdf document generator function', async () => { + await createCourtIssuedOrderPdfFromHtmlInteractor({ + applicationContext, + }); + expect(applicationContext.getDocumentGenerators().order).toHaveBeenCalled(); + }); + + it('returns the pdf url from the temp documents bucket', async () => { + const result = await createCourtIssuedOrderPdfFromHtmlInteractor({ + applicationContext, + }); + + expect( + applicationContext.getUseCaseHelpers().saveFileAndGenerateUrl, + ).toBeCalledWith(expect.objectContaining({ useTempBucket: true })); + expect(result).toEqual(mockPdfUrl); }); }); diff --git a/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.js b/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.js index b1290cfcfed..6c597fe7427 100644 --- a/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.js +++ b/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.js @@ -89,7 +89,7 @@ exports.fileCourtIssuedOrderInteractor = async ({ applicationContext, document: Buffer.from(JSON.stringify(contentToStore)), documentId: documentContentsId, - useTempBucket: true, + useTempBucket: false, }); if (documentMetadata.draftState) { diff --git a/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.test.js b/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.test.js index 00f87444bdb..a8e3c05db96 100644 --- a/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.test.js +++ b/shared/src/business/useCases/courtIssuedOrder/fileCourtIssuedOrderInteractor.test.js @@ -5,6 +5,7 @@ const { const { fileCourtIssuedOrderInteractor, } = require('./fileCourtIssuedOrderInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); describe('fileCourtIssuedOrderInteractor', () => { @@ -38,42 +39,42 @@ describe('fileCourtIssuedOrderInteractor', () => { docketNumber: '45678-18', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, { docketNumber: '45678-18', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, { docketNumber: '45678-18', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, ], filingType: 'Myself', partyType: 'Petitioner', preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: 'ddd6c900-388b-4151-8014-b3378076bfb0', }; beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue( new User({ - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.petitionsClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }), ); applicationContext.getPersistenceGateway().getUserById.mockReturnValue( new User({ - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.petitionsClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }), ); @@ -188,8 +189,11 @@ describe('fileCourtIssuedOrderInteractor', () => { }); expect( - applicationContext.getPersistenceGateway().saveDocumentFromLambda, - ).toHaveBeenCalled(); + applicationContext.getPersistenceGateway().saveDocumentFromLambda.mock + .calls[0][0], + ).toMatchObject({ + useTempBucket: false, + }); expect( applicationContext.getPersistenceGateway().updateCase.mock.calls[0][0] .caseToUpdate.documents[3].documentContents, diff --git a/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.js b/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.js index 7836250f48f..cc2c6f60535 100644 --- a/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.js +++ b/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.js @@ -55,11 +55,11 @@ exports.updateCourtIssuedOrderInteractor = async ({ richText: documentMetadata.draftState.richText, }; - applicationContext.getPersistenceGateway().saveDocumentFromLambda({ + await applicationContext.getPersistenceGateway().saveDocumentFromLambda({ applicationContext, document: Buffer.from(JSON.stringify(contentToStore)), documentId: documentContentsId, - useTempBucket: true, + useTempBucket: false, }); delete documentMetadata.documentContents; @@ -75,12 +75,17 @@ exports.updateCourtIssuedOrderInteractor = async ({ freeText: documentMetadata.freeText, }; + const numberOfPages = await applicationContext + .getUseCaseHelpers() + .countPagesInDocument({ applicationContext, documentId: documentIdToEdit }); + const documentEntity = new Document( { ...currentDocument, ...editableFields, documentId: documentIdToEdit, filedBy: user.name, + numberOfPages, relationship: 'primaryDocument', userId: user.userId, }, diff --git a/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.test.js b/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.test.js index c4512235e25..9eee1fa8502 100644 --- a/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.test.js +++ b/shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor.test.js @@ -4,10 +4,12 @@ const { const { updateCourtIssuedOrderInteractor, } = require('./updateCourtIssuedOrderInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); describe('updateCourtIssuedOrderInteractor', () => { - let mockUser; + let mockCurrentUser; + let mockUserById; let caseRecord = { caseCaption: 'Caption', @@ -40,50 +42,55 @@ describe('updateCourtIssuedOrderInteractor', () => { documentContentsId: '442f46fd-727b-485c-8998-a0138593cebe', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', - userId: 'irsPractitioner', + userId: '2db02773-6583-42d8-ab91-52529d1993cf', }, { docketNumber: '45678-18', documentId: 'a75e4cc8-deed-42d0-b7b0-3846004fe3f9', documentType: 'Answer', - userId: 'irsPractitioner', + userId: '2db02773-6583-42d8-ab91-52529d1993cf', }, { docketNumber: '45678-18', documentId: 'd3cc11ab-bbee-4d09-bc66-da267f3cfd07', documentType: 'Answer', - userId: 'irsPractitioner', + userId: '2db02773-6583-42d8-ab91-52529d1993cf', }, ], filingType: 'Myself', partyType: 'Petitioner', preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '3433e36f-3b50-4c92-aa55-6efb4e432883', }; beforeEach(() => { - applicationContext.environment.stage = 'local'; - applicationContext.getCurrentUser.mockImplementation(() => mockUser); - applicationContext - .getPersistenceGateway() - .getCaseByCaseId.mockResolvedValue(caseRecord); - }); - - it('should throw an error if not authorized', async () => { - mockUser = new User({ + mockCurrentUser = new User({ name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + role: ROLES.petitionsClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); + applicationContext.getCurrentUser.mockImplementation(() => mockCurrentUser); + + mockUserById = { + name: 'bob', + userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + }; + applicationContext .getPersistenceGateway() - .updateCase.mockResolvedValue(caseRecord); + .getUserById.mockImplementation(() => mockUserById); + applicationContext .getPersistenceGateway() - .getUserById.mockResolvedValue({ name: 'bob' }); + .getCaseByCaseId.mockResolvedValue(caseRecord); + }); + + it('should throw an error if not authorized', async () => { + mockCurrentUser.role = ROLES.privatePractitioner; + mockUserById = { name: 'bob' }; await expect( updateCourtIssuedOrderInteractor({ @@ -98,16 +105,7 @@ describe('updateCourtIssuedOrderInteractor', () => { }); it('should throw an error if document is not found', async () => { - mockUser = new User({ - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - - applicationContext.getPersistenceGateway().getUserById.mockResolvedValue({ - name: 'bob', - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); + applicationContext.getPersistenceGateway().getUserById.mockResolvedValue(); await expect( updateCourtIssuedOrderInteractor({ @@ -122,17 +120,6 @@ describe('updateCourtIssuedOrderInteractor', () => { }); it('update existing document within case', async () => { - mockUser = new User({ - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - - applicationContext.getPersistenceGateway().getUserById.mockResolvedValue({ - name: 'bob', - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - await updateCourtIssuedOrderInteractor({ applicationContext, documentIdToEdit: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -152,17 +139,6 @@ describe('updateCourtIssuedOrderInteractor', () => { }); it('stores documentContents in S3 if present', async () => { - mockUser = new User({ - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - - applicationContext.getPersistenceGateway().getUserById.mockResolvedValue({ - name: 'bob', - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - await updateCourtIssuedOrderInteractor({ applicationContext, documentIdToEdit: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -179,8 +155,9 @@ describe('updateCourtIssuedOrderInteractor', () => { }); expect( - applicationContext.getPersistenceGateway().saveDocumentFromLambda, - ).toBeCalled(); + applicationContext.getPersistenceGateway().saveDocumentFromLambda.mock + .calls[0][0], + ).toMatchObject({ useTempBucket: false }); expect( applicationContext.getPersistenceGateway().updateCase.mock.calls[0][0] .caseToUpdate.documents[2].documentContents, @@ -192,17 +169,6 @@ describe('updateCourtIssuedOrderInteractor', () => { }); it('does not update non-editable fields on document', async () => { - mockUser = new User({ - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - - applicationContext.getPersistenceGateway().getUserById.mockResolvedValue({ - name: 'bob', - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - await updateCourtIssuedOrderInteractor({ applicationContext, documentIdToEdit: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', diff --git a/shared/src/business/useCases/createCaseFromPaperInteractor.js b/shared/src/business/useCases/createCaseFromPaperInteractor.js index 41af2fbb6f9..08e2553a0bf 100644 --- a/shared/src/business/useCases/createCaseFromPaperInteractor.js +++ b/shared/src/business/useCases/createCaseFromPaperInteractor.js @@ -5,6 +5,7 @@ const { const { Case } = require('../entities/cases/Case'); const { CaseInternal } = require('../entities/cases/CaseInternal'); const { Document } = require('../entities/Document'); +const { INITIAL_DOCUMENT_TYPES } = require('../entities/EntityConstants'); const { Message } = require('../entities/Message'); const { replaceBracketed } = require('../utilities/replaceBracketed'); const { UnauthorizedError } = require('../../errors/errors'); @@ -33,7 +34,7 @@ const addPetitionDocumentWithWorkItemToCase = ({ ...documentEntity.toRawObject(), createdAt: documentEntity.createdAt, }, - isInitializeCase: documentEntity.isPetitionDocument(), + isInitializeCase: true, isQC: true, section: user.section, sentBy: user.name, @@ -60,7 +61,6 @@ const addPetitionDocumentWithWorkItemToCase = ({ caseToAdd.addDocument(documentEntity, { applicationContext }); return { - message: newMessage, workItem: workItemEntity, }; }; @@ -141,8 +141,8 @@ exports.createCaseFromPaperInteractor = async ({ { createdAt: caseToAdd.receivedAt, documentId: petitionFileId, - documentType: Document.INITIAL_DOCUMENT_TYPES.petition.documentType, - eventCode: Document.INITIAL_DOCUMENT_TYPES.petition.eventCode, + documentType: INITIAL_DOCUMENT_TYPES.petition.documentType, + eventCode: INITIAL_DOCUMENT_TYPES.petition.eventCode, filingDate: caseToAdd.receivedAt, isPaper: true, mailingDate: petitionEntity.mailingDate, @@ -158,10 +158,7 @@ exports.createCaseFromPaperInteractor = async ({ { applicationContext }, ); - const { - message: newMessage, - workItem: newWorkItem, - } = addPetitionDocumentWithWorkItemToCase({ + const { workItem: newWorkItem } = addPetitionDocumentWithWorkItemToCase({ applicationContext, caseToAdd, documentEntity: petitionDocumentEntity, @@ -171,7 +168,7 @@ exports.createCaseFromPaperInteractor = async ({ if (applicationForWaiverOfFilingFeeFileId) { let { documentTitle, - } = Document.INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee; + } = INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee; const applicationForWaiverOfFilingFeeDocumentEntity = new Document( { @@ -179,11 +176,9 @@ exports.createCaseFromPaperInteractor = async ({ documentId: applicationForWaiverOfFilingFeeFileId, documentTitle, documentType: - Document.INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee - .documentType, + INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee.documentType, eventCode: - Document.INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee - .eventCode, + INITIAL_DOCUMENT_TYPES.applicationForWaiverOfFilingFee.eventCode, filingDate: caseToAdd.receivedAt, isPaper: true, mailingDate: petitionEntity.mailingDate, @@ -205,9 +200,7 @@ exports.createCaseFromPaperInteractor = async ({ } if (requestForPlaceOfTrialFileId) { - let { - documentTitle, - } = Document.INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial; + let { documentTitle } = INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial; if (caseToAdd.preferredTrialCity) { documentTitle = replaceBracketed( documentTitle, @@ -221,9 +214,8 @@ exports.createCaseFromPaperInteractor = async ({ documentId: requestForPlaceOfTrialFileId, documentTitle, documentType: - Document.INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.documentType, - eventCode: - Document.INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, + INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.documentType, + eventCode: INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, filingDate: caseToAdd.receivedAt, isPaper: true, mailingDate: petitionEntity.mailingDate, @@ -249,8 +241,8 @@ exports.createCaseFromPaperInteractor = async ({ { createdAt: caseToAdd.receivedAt, documentId: stinFileId, - documentType: Document.INITIAL_DOCUMENT_TYPES.stin.documentType, - eventCode: Document.INITIAL_DOCUMENT_TYPES.stin.eventCode, + documentType: INITIAL_DOCUMENT_TYPES.stin.documentType, + eventCode: INITIAL_DOCUMENT_TYPES.stin.eventCode, filingDate: caseToAdd.receivedAt, isPaper: true, mailingDate: petitionEntity.mailingDate, @@ -276,10 +268,8 @@ exports.createCaseFromPaperInteractor = async ({ { createdAt: caseToAdd.receivedAt, documentId: ownershipDisclosureFileId, - documentType: - Document.INITIAL_DOCUMENT_TYPES.ownershipDisclosure.documentType, - eventCode: - Document.INITIAL_DOCUMENT_TYPES.ownershipDisclosure.eventCode, + documentType: INITIAL_DOCUMENT_TYPES.ownershipDisclosure.documentType, + eventCode: INITIAL_DOCUMENT_TYPES.ownershipDisclosure.eventCode, filingDate: caseToAdd.receivedAt, isPaper: true, mailingDate: petitionEntity.mailingDate, @@ -305,7 +295,6 @@ exports.createCaseFromPaperInteractor = async ({ }), applicationContext.getPersistenceGateway().saveWorkItemForPaper({ applicationContext, - messageId: newMessage.messageId, workItem: newWorkItem.validate().toRawObject(), }), ]); diff --git a/shared/src/business/useCases/createCaseFromPaperInteractor.test.js b/shared/src/business/useCases/createCaseFromPaperInteractor.test.js index b7d5a0fb934..805e21f0e8a 100644 --- a/shared/src/business/useCases/createCaseFromPaperInteractor.test.js +++ b/shared/src/business/useCases/createCaseFromPaperInteractor.test.js @@ -4,8 +4,8 @@ const { createCaseFromPaperInteractor, } = require('./createCaseFromPaperInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); +const { PARTY_TYPES, PAYMENT_STATUS } = require('../entities/EntityConstants'); +const { ROLES } = require('../entities/EntityConstants'); const { UnauthorizedError } = require('../../errors/errors'); const { User } = require('../entities/User'); @@ -24,7 +24,7 @@ describe('createCaseFromPaperInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -35,7 +35,7 @@ describe('createCaseFromPaperInteractor', () => { applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -87,10 +87,10 @@ describe('createCaseFromPaperInteractor', () => { hasIrsNotice: true, irsNoticeDate: DATE, mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'petitionFile.pdf'), petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, preferredTrialCity: 'Fresno, California', procedureType: 'Small', receivedAt: new Date().toISOString(), @@ -134,10 +134,10 @@ describe('createCaseFromPaperInteractor', () => { hasIrsNotice: true, irsNoticeDate: DATE, mailingDate: 'test', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'petitionFile.pdf'), petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, preferredTrialCity: 'Fresno, California', procedureType: 'Small', receivedAt: new Date().toISOString(), @@ -181,10 +181,10 @@ describe('createCaseFromPaperInteractor', () => { hasIrsNotice: true, irsNoticeDate: DATE, mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'petitionFile.pdf'), petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, preferredTrialCity: 'Fresno, California', procedureType: 'Small', receivedAt: new Date().toISOString(), @@ -229,10 +229,10 @@ describe('createCaseFromPaperInteractor', () => { hasIrsNotice: true, irsNoticeDate: DATE, mailingDate: 'testing', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'petitionFile.pdf'), petitionFileSize: 1, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, preferredTrialCity: 'Fresno, California', procedureType: 'Small', receivedAt: new Date().toISOString(), diff --git a/shared/src/business/useCases/createCaseInteractor.js b/shared/src/business/useCases/createCaseInteractor.js index 5b5d737bd81..da0b05fb72f 100644 --- a/shared/src/business/useCases/createCaseInteractor.js +++ b/shared/src/business/useCases/createCaseInteractor.js @@ -8,10 +8,12 @@ const { const { Case } = require('../entities/cases/Case'); const { DocketRecord } = require('../entities/DocketRecord'); const { Document } = require('../entities/Document'); +const { INITIAL_DOCUMENT_TYPES } = require('../entities/EntityConstants'); const { Message } = require('../entities/Message'); -const { PETITIONS_SECTION } = require('../entities/WorkQueue'); +const { PETITIONS_SECTION } = require('../entities/EntityConstants'); +const { ROLES } = require('../entities/EntityConstants'); const { UnauthorizedError } = require('../../errors/errors'); -const { User } = require('../entities/User'); +const { UserCase } = require('../entities/UserCase'); const { WorkItem } = require('../entities/WorkItem'); const addPetitionDocumentToCase = ({ @@ -38,7 +40,8 @@ const addPetitionDocumentToCase = ({ isInitializeCase: true, isQC: true, section: PETITIONS_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, }, { applicationContext }, ); @@ -103,7 +106,7 @@ exports.createCaseInteractor = async ({ ); let privatePractitioners = []; - if (user.role === User.ROLES.privatePractitioner) { + if (user.role === ROLES.privatePractitioner) { const practitionerUser = await applicationContext .getPersistenceGateway() .getUserById({ @@ -134,6 +137,7 @@ exports.createCaseInteractor = async ({ { docketNumber, isPaper: false, + orderForFilingFee: true, ...petitionEntity.toRawObject(), privatePractitioners, userId: user.userId, @@ -144,12 +148,13 @@ exports.createCaseInteractor = async ({ ); caseToAdd.caseCaption = Case.getCaseCaption(caseToAdd); + caseToAdd.initialCaption = caseToAdd.caseCaption; const petitionDocumentEntity = new Document( { documentId: petitionFileId, - documentType: Document.INITIAL_DOCUMENT_TYPES.petition.documentType, - eventCode: Document.INITIAL_DOCUMENT_TYPES.petition.eventCode, + documentType: INITIAL_DOCUMENT_TYPES.petition.documentType, + eventCode: INITIAL_DOCUMENT_TYPES.petition.eventCode, filingDate: caseToAdd.createdAt, partyPrimary: true, partySecondary, @@ -174,8 +179,7 @@ exports.createCaseInteractor = async ({ new DocketRecord( { description: `Request for Place of Trial at ${caseToAdd.preferredTrialCity}`, - eventCode: - Document.INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, + eventCode: INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.eventCode, filingDate: caseToAdd.createdAt, }, { applicationContext }, @@ -185,8 +189,8 @@ exports.createCaseInteractor = async ({ const stinDocumentEntity = new Document( { documentId: stinFileId, - documentType: Document.INITIAL_DOCUMENT_TYPES.stin.documentType, - eventCode: Document.INITIAL_DOCUMENT_TYPES.stin.eventCode, + documentType: INITIAL_DOCUMENT_TYPES.stin.documentType, + eventCode: INITIAL_DOCUMENT_TYPES.stin.eventCode, filingDate: caseToAdd.createdAt, partyPrimary: true, partySecondary, @@ -206,10 +210,8 @@ exports.createCaseInteractor = async ({ const odsDocumentEntity = new Document( { documentId: ownershipDisclosureFileId, - documentType: - Document.INITIAL_DOCUMENT_TYPES.ownershipDisclosure.documentType, - eventCode: - Document.INITIAL_DOCUMENT_TYPES.ownershipDisclosure.eventCode, + documentType: INITIAL_DOCUMENT_TYPES.ownershipDisclosure.documentType, + eventCode: INITIAL_DOCUMENT_TYPES.ownershipDisclosure.eventCode, filingDate: caseToAdd.createdAt, partyPrimary: true, partySecondary, @@ -231,6 +233,15 @@ exports.createCaseInteractor = async ({ caseToCreate: caseToAdd.validate().toRawObject(), }); + const userCaseEntity = new UserCase(caseToAdd); + + await applicationContext.getPersistenceGateway().associateUserWithCase({ + applicationContext, + caseId: caseToAdd.caseId, + userCase: userCaseEntity.validate().toRawObject(), + userId: user.userId, + }); + await applicationContext.getPersistenceGateway().saveWorkItemForNonPaper({ applicationContext, workItem: newWorkItem.validate().toRawObject(), diff --git a/shared/src/business/useCases/createCaseInteractor.test.js b/shared/src/business/useCases/createCaseInteractor.test.js index e9d315f846a..9ef0a7d9a43 100644 --- a/shared/src/business/useCases/createCaseInteractor.test.js +++ b/shared/src/business/useCases/createCaseInteractor.test.js @@ -1,6 +1,6 @@ const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { createCaseInteractor } = require('./createCaseInteractor'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('createCaseInteractor', () => { @@ -9,7 +9,7 @@ describe('createCaseInteractor', () => { beforeEach(() => { user = new User({ name: 'Test Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -39,7 +39,7 @@ describe('createCaseInteractor', () => { caseType: 'Other', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Small', }, @@ -75,7 +75,7 @@ describe('createCaseInteractor', () => { contactSecondary: {}, filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, preferredTrialCity: 'Fresno, California', @@ -89,6 +89,9 @@ describe('createCaseInteractor', () => { expect(result).toBeDefined(); expect(applicationContext.getPersistenceGateway().createCase).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().associateUserWithCase, + ).toBeCalled(); expect( applicationContext.getPersistenceGateway().saveWorkItemForNonPaper, ).toBeCalled(); @@ -97,7 +100,7 @@ describe('createCaseInteractor', () => { it('should create a case successfully as a practitioner', async () => { user = new User({ name: 'Mister Peanutbutter', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -122,7 +125,7 @@ describe('createCaseInteractor', () => { contactSecondary: {}, filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, preferredTrialCity: 'Fresno, California', @@ -148,7 +151,7 @@ describe('createCaseInteractor', () => { it('should create a case with contact primary and secondary successfully as a practitioner', async () => { user = new User({ name: 'Carole Baskin', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -184,7 +187,7 @@ describe('createCaseInteractor', () => { filingType: 'Myself and my spouse', hasIrsNotice: true, isSpouseDeceased: 'No', - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, preferredTrialCity: 'Fresno, California', diff --git a/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.js b/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.js index 10018f10c6b..6d67a6864fe 100644 --- a/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.js +++ b/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.js @@ -5,11 +5,12 @@ const { const { capitalize, omit } = require('lodash'); const { Case } = require('../../entities/cases/Case'); const { createISODateString } = require('../../utilities/DateHandler'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); const { DocketRecord } = require('../../entities/DocketRecord'); const { Document } = require('../../entities/Document'); const { Message } = require('../../entities/Message'); const { NotFoundError, UnauthorizedError } = require('../../../errors/errors'); +const { TRANSCRIPT_EVENT_CODE } = require('../../entities/EntityConstants'); const { WorkItem } = require('../../entities/WorkItem'); /** @@ -57,10 +58,14 @@ exports.fileCourtIssuedDocketEntryInteractor = async ({ .getUserById({ applicationContext, userId: authorizedUser.userId }); let secondaryDate; - if (documentMeta.eventCode === Document.TRANSCRIPT_EVENT_CODE) { + if (documentMeta.eventCode === TRANSCRIPT_EVENT_CODE) { secondaryDate = documentMeta.date; } + const numberOfPages = await applicationContext + .getUseCaseHelpers() + .countPagesInDocument({ applicationContext, documentId }); + const documentEntity = new Document( { ...omit(document, 'filedBy'), @@ -73,6 +78,7 @@ exports.fileCourtIssuedDocketEntryInteractor = async ({ freeText: documentMeta.freeText, isFileAttached: true, judge: documentMeta.judge, + numberOfPages, scenario: documentMeta.scenario, secondaryDate, serviceStamp: documentMeta.serviceStamp, @@ -100,7 +106,8 @@ exports.fileCourtIssuedDocketEntryInteractor = async ({ inProgress: true, isQC: true, section: DOCKET_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, }, { applicationContext }, ); @@ -137,6 +144,7 @@ exports.fileCourtIssuedDocketEntryInteractor = async ({ editState: JSON.stringify(documentMeta), eventCode: documentEntity.eventCode, filingDate: documentEntity.filingDate || createISODateString(), + numberOfPages, }, { applicationContext }, ), diff --git a/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.test.js b/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.test.js index fc6fb2b7b17..1b5c0134a0e 100644 --- a/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.test.js +++ b/shared/src/business/useCases/docketEntry/fileCourtIssuedDocketEntryInteractor.test.js @@ -1,20 +1,22 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { + AUTOMATIC_BLOCKED_REASONS, + PARTY_TYPES, +} = require('../../entities/EntityConstants'); const { fileCourtIssuedDocketEntryInteractor, } = require('./fileCourtIssuedDocketEntryInteractor'); -const { Case } = require('../../entities/cases/Case'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('fileCourtIssuedDocketEntryInteractor', () => { let caseRecord; beforeEach(() => { applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -55,7 +57,7 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { documentTitle: 'Answer', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, { docketNumber: '45678-18', @@ -63,7 +65,7 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { documentTitle: 'Answer', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, { docketNumber: '45678-18', @@ -71,7 +73,7 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { documentTitle: 'Answer', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, { docketNumber: '45678-18', @@ -79,7 +81,7 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { documentTitle: 'Order', documentType: 'Order', eventCode: 'O', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, { docketNumber: '45678-18', @@ -87,7 +89,7 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { documentTitle: 'Order to Show Cause', documentType: 'Order to Show Cause', eventCode: 'OSC', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, { docketNumber: '45678-18', @@ -95,15 +97,15 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { documentTitle: 'Transcript of [anything] on [date]', documentType: 'TRAN - Transcript', eventCode: 'TRAN', - userId: 'irsPractitioner', + userId: 'e3bb51b1-bb93-494b-8a20-8bce8327fd99', }, ], filingType: 'Myself', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '8100e22a-c7f2-4574-b4f6-eb092fca9f35', }; }); @@ -124,8 +126,8 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { it('should throw an error if the document is not found on the case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -141,10 +143,10 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { ).rejects.toThrow('Document not found'); }); - it('should call updateCase, createUserInboxRecord, and createSectionInboxRecord', async () => { + it('should call countPagesInDocument, updateCase, createUserInboxRecord, and createSectionInboxRecord', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -174,8 +176,8 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { it('should call updateCase and set the case as automatic blocked if the document is a tracked document', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -201,7 +203,7 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pending, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pending, }); expect( applicationContext.getPersistenceGateway() @@ -218,8 +220,8 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { }, ]); applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -245,7 +247,7 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, }); expect( applicationContext.getPersistenceGateway() @@ -255,8 +257,8 @@ describe('fileCourtIssuedDocketEntryInteractor', () => { it('should set secondaryDate on the created document if the eventCode is TRAN', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); diff --git a/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.js b/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.js index 900618a3458..e469ee4d0a6 100644 --- a/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.js +++ b/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.js @@ -4,12 +4,12 @@ const { } = require('../../../authorization/authorizationClientService'); const { capitalize, pick } = require('lodash'); const { Case } = require('../../entities/cases/Case'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); const { DocketRecord } = require('../../entities/DocketRecord'); const { Document } = require('../../entities/Document'); const { Message } = require('../../entities/Message'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const { WorkItem } = require('../../entities/WorkItem'); /** @@ -80,7 +80,7 @@ exports.fileDocketEntryInteractor = async ({ secondarySupportingDocumentMetadata.eventCode = 'MISL'; } - [ + const documentsToFile = [ [primaryDocumentFileId, primaryDocumentMetadata, 'primaryDocument'], [ supportingDocumentFileId, @@ -93,7 +93,11 @@ exports.fileDocketEntryInteractor = async ({ secondarySupportingDocumentMetadata, 'secondarySupportingDocument', ], - ].forEach(([documentId, metadata, relationship]) => { + ]; + + for (let document of documentsToFile) { + const [documentId, metadata, relationship] = document; + if (documentId && metadata) { const documentEntity = new Document( { @@ -128,9 +132,10 @@ exports.fileDocketEntryInteractor = async ({ createdAt: documentEntity.createdAt, }, isQC: true, - isRead: user.role !== User.ROLES.privatePractitioner, + isRead: user.role !== ROLES.privatePractitioner, section: DOCKET_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, }, { applicationContext }, ); @@ -186,7 +191,7 @@ exports.fileDocketEntryInteractor = async ({ ), ); } - }); + } caseEntity = await applicationContext .getUseCaseHelpers() diff --git a/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.test.js b/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.test.js index 4749d4dec21..ccf414d46ea 100644 --- a/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.test.js +++ b/shared/src/business/useCases/docketEntry/fileDocketEntryInteractor.test.js @@ -1,15 +1,14 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); -const { Case } = require('../../entities/cases/Case'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); +const { AUTOMATIC_BLOCKED_REASONS } = require('../../entities/EntityConstants'); const { fileDocketEntryInteractor } = require('./fileDocketEntryInteractor'); -const { User } = require('../../entities/User'); +const { PARTY_TYPES, ROLES } = require('../../entities/EntityConstants'); describe('fileDocketEntryInteractor', () => { const user = { - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; @@ -39,7 +38,7 @@ describe('fileDocketEntryInteractor', () => { documentTitle: 'Answer', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', }, { docketNumber: '45678-18', @@ -47,7 +46,7 @@ describe('fileDocketEntryInteractor', () => { documentTitle: 'Answer', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', }, { docketNumber: '45678-18', @@ -55,15 +54,15 @@ describe('fileDocketEntryInteractor', () => { documentTitle: 'Answer', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', }, ], filingType: 'Myself', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', }; applicationContext @@ -104,6 +103,7 @@ describe('fileDocketEntryInteractor', () => { documentTitle: 'Memorandum in Support', documentType: 'Memorandum in Support', eventCode: 'MISL', + isFileAttached: true, isPaper: true, }, primaryDocumentFileId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -141,16 +141,19 @@ describe('fileDocketEntryInteractor', () => { documentTitle: 'Memorandum in Support', documentType: 'Memorandum in Support', eventCode: 'MISL', + isFileAttached: true, lodged: true, secondaryDocument: { documentTitle: 'Memorandum in Support', documentType: 'Memorandum in Support', eventCode: 'MISL', + isFileAttached: true, }, secondarySupportingDocumentMetadata: { documentTitle: 'Memorandum in Support', documentType: 'Memorandum in Support', eventCode: 'MISL', + isFileAttached: true, }, }, primaryDocumentFileId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -195,7 +198,7 @@ describe('fileDocketEntryInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pending, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pending, }); expect( applicationContext.getPersistenceGateway() @@ -228,7 +231,7 @@ describe('fileDocketEntryInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, }); expect( applicationContext.getPersistenceGateway() diff --git a/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.js b/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.js index e5f08a73f9a..cbc2e586005 100644 --- a/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.js +++ b/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.js @@ -7,6 +7,7 @@ const { DocketRecord } = require('../../entities/DocketRecord'); const { Document } = require('../../entities/Document'); const { NotFoundError, UnauthorizedError } = require('../../../errors/errors'); const { omit } = require('lodash'); +const { TRANSCRIPT_EVENT_CODE } = require('../../entities/EntityConstants'); /** * @@ -53,7 +54,7 @@ exports.updateCourtIssuedDocketEntryInteractor = async ({ .getUserById({ applicationContext, userId: authorizedUser.userId }); let secondaryDate; - if (documentMeta.eventCode === Document.TRANSCRIPT_EVENT_CODE) { + if (documentMeta.eventCode === TRANSCRIPT_EVENT_CODE) { secondaryDate = documentMeta.date; } diff --git a/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.test.js b/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.test.js index b4aed19525c..595ea34472f 100644 --- a/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.test.js +++ b/shared/src/business/useCases/docketEntry/updateCourtIssuedDocketEntryInteractor.test.js @@ -2,9 +2,8 @@ import { updateCourtIssuedDocketEntryInteractor } from './updateCourtIssuedDocke const { applicationContext, } = require('../../test/createTestApplicationContext'); -const { Case } = require('../../entities/cases/Case'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); -const { User } = require('../../entities/User'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); +const { PARTY_TYPES, ROLES } = require('../../entities/EntityConstants'); describe('updateCourtIssuedDocketEntryInteractor', () => { let caseRecord; @@ -41,31 +40,31 @@ describe('updateCourtIssuedDocketEntryInteractor', () => { docketNumber: '45678-18', documentId: '30413c1e-9a71-4c22-8c11-41f8689313ae', documentType: 'Answer', - userId: 'irsPractitioner', + userId: 'be32eee7-4c0c-48bf-b2bd-7000ebb6941f', }, { docketNumber: '45678-18', documentId: 'e27d2d4e-f768-4167-b2c9-989dccbbb738', documentType: 'Answer', - userId: 'irsPractitioner', + userId: 'be32eee7-4c0c-48bf-b2bd-7000ebb6941f', }, { docketNumber: '45678-18', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', - userId: 'irsPractitioner', + userId: 'be32eee7-4c0c-48bf-b2bd-7000ebb6941f', }, { docketNumber: '45678-18', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335ba', documentType: 'Order', - userId: 'irsPractitioner', + userId: 'be32eee7-4c0c-48bf-b2bd-7000ebb6941f', workItems: [ { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, caseTitle: 'Johnny Joe Jacobson', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -83,13 +82,13 @@ describe('updateCourtIssuedDocketEntryInteractor', () => { documentTitle: 'Transcript of [anything] on [date]', documentType: 'TRAN - Transcript', eventCode: 'TRAN', - userId: 'irsPractitioner', + userId: 'be32eee7-4c0c-48bf-b2bd-7000ebb6941f', workItems: [ { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', assigneeName: 'bob', caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-18', docketNumberSuffix: 'S', document: {}, @@ -102,15 +101,15 @@ describe('updateCourtIssuedDocketEntryInteractor', () => { }, ], filingType: 'Myself', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '8100e22a-c7f2-4574-b4f6-eb092fca9f35', }; applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ - name: 'Olivia Jade', + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -136,8 +135,8 @@ describe('updateCourtIssuedDocketEntryInteractor', () => { it('should throw an error if the document is not found on the case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -155,8 +154,8 @@ describe('updateCourtIssuedDocketEntryInteractor', () => { it('should call updateCase, createUserInboxRecord, and createSectionInboxRecord', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -182,8 +181,8 @@ describe('updateCourtIssuedDocketEntryInteractor', () => { it('should set secondaryDate on the created document if the eventCode is TRAN', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); @@ -215,8 +214,8 @@ describe('updateCourtIssuedDocketEntryInteractor', () => { it('should not update non-editable fields on the document', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); diff --git a/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.js b/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.js index f78ccefa66f..9e1da2b5909 100644 --- a/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.js +++ b/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.js @@ -3,7 +3,7 @@ const { ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); const { Case } = require('../../entities/cases/Case'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); const { DocketRecord } = require('../../entities/DocketRecord'); const { Document } = require('../../entities/Document'); const { omit } = require('lodash'); diff --git a/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.test.js b/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.test.js index bf8a580276a..ad79e045051 100644 --- a/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.test.js +++ b/shared/src/business/useCases/docketEntry/updateDocketEntryInteractor.test.js @@ -4,9 +4,11 @@ const { const { updateDocketEntryInteractor, } = require('./updateDocketEntryInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('updateDocketEntryInteractor', () => { + let mockCurrentUser; + const workItem = { caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', docketNumber: '45678-18', @@ -42,7 +44,7 @@ describe('updateDocketEntryInteractor', () => { { description: 'first record', docketRecordId: '8675309b-18d0-43ec-bafb-654e83405411', - documentId: '8675309b-18d0-43ec-bafb-654e83405411', + documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', eventCode: 'P', filingDate: '2018-03-01T00:01:00.000Z', index: 1, @@ -75,12 +77,32 @@ describe('updateDocketEntryInteractor', () => { partyType: 'Petitioner', preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; + beforeEach(() => { + mockCurrentUser = { + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, + section: 'docket', + userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + }; + + applicationContext.getCurrentUser.mockImplementation(() => mockCurrentUser); + applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, + section: 'docket', + userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + }); + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(caseRecord); + }); + it('should throw an error if not authorized', async () => { - applicationContext.getCurrentUser.mockReturnValue({}); + mockCurrentUser = {}; await expect( updateDocketEntryInteractor({ @@ -95,28 +117,14 @@ describe('updateDocketEntryInteractor', () => { }); it('updates the workitem without updating the document if no file is attached', async () => { - applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, - section: 'docket', - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, - section: 'docket', - userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - }); - applicationContext - .getPersistenceGateway() - .getCaseByCaseId.mockReturnValue(caseRecord); - await expect( updateDocketEntryInteractor({ applicationContext, documentMetadata: { caseId: caseRecord.caseId, + documentTitle: 'My Document', documentType: 'Memorandum in Support', + eventCode: 'P', isFileAttached: false, }, primaryDocumentFileId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -142,7 +150,9 @@ describe('updateDocketEntryInteractor', () => { applicationContext, documentMetadata: { caseId: caseRecord.caseId, + documentTitle: 'My Document', documentType: 'Memorandum in Support', + eventCode: 'P', isFileAttached: true, }, primaryDocumentFileId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -168,7 +178,9 @@ describe('updateDocketEntryInteractor', () => { applicationContext, documentMetadata: { caseId: caseRecord.caseId, + documentTitle: 'My Document', documentType: 'Memorandum in Support', + eventCode: 'P', isPaper: true, }, primaryDocumentFileId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', diff --git a/shared/src/business/useCases/docketEntry/updateDocketEntryMetaInteractor.test.js b/shared/src/business/useCases/docketEntry/updateDocketEntryMetaInteractor.test.js index 4b97155a413..95595ab175a 100644 --- a/shared/src/business/useCases/docketEntry/updateDocketEntryMetaInteractor.test.js +++ b/shared/src/business/useCases/docketEntry/updateDocketEntryMetaInteractor.test.js @@ -4,8 +4,8 @@ const { } = require('../../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../../test/mockCase'); const { NotFoundError } = require('../../../errors/errors'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('updateDocketEntryMetaInteractor', () => { let docketRecord; @@ -70,7 +70,7 @@ describe('updateDocketEntryMetaInteractor', () => { }; applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'abcba5a9-b37b-479d-9201-067ec6e33abc', }); diff --git a/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.js b/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.js index 564a195c99a..653ec89c552 100644 --- a/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.js +++ b/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.js @@ -4,6 +4,10 @@ const { const { aggregatePartiesForService, } = require('../../utilities/aggregatePartiesForService'); +const { + CONTACT_CHANGE_DOCUMENT_TYPES, + NOTICE_OF_DOCKET_CHANGE, +} = require('../../entities/EntityConstants'); const { formatDocument, getFilingsAndProceedings, @@ -16,13 +20,13 @@ const { ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); const { Case } = require('../../entities/cases/Case'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); +const { CASE_CAPTION_POSTFIX } = require('../../entities/EntityConstants'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); const { DocketRecord } = require('../../entities/DocketRecord'); const { Document } = require('../../entities/Document'); const { formatDateString } = require('../../utilities/DateHandler'); const { getCaseCaptionMeta } = require('../../utilities/getCaseCaptionMeta'); const { omit } = require('lodash'); -const { PDFDocument } = require('pdf-lib'); const { replaceBracketed } = require('../../utilities/replaceBracketed'); const { UnauthorizedError } = require('../../../errors/errors'); @@ -40,6 +44,8 @@ exports.completeDocketEntryQCInteractor = async ({ }) => { const authorizedUser = applicationContext.getCurrentUser(); + const { PDFDocument } = await applicationContext.getPdfLib(); + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.DOCKET_ENTRY)) { throw new UnauthorizedError('Unauthorized'); } @@ -143,7 +149,7 @@ exports.completeDocketEntryQCInteractor = async ({ const docketChangeInfo = { caseCaptionExtension, - caseCaptionWithPostfix: `${caseToUpdate.caseCaption} ${Case.CASE_CAPTION_POSTFIX}`, + caseCaptionWithPostfix: `${caseToUpdate.caseCaption} ${CASE_CAPTION_POSTFIX}`, caseTitle, docketEntryIndex: docketRecordIndexUpdated, docketNumber: `${caseToUpdate.docketNumber}${ @@ -233,11 +239,7 @@ exports.completeDocketEntryQCInteractor = async ({ let paperServicePdfUrl; let paperServiceDocumentTitle; - if ( - Document.CONTACT_CHANGE_DOCUMENT_TYPES.includes( - updatedDocument.documentType, - ) - ) { + if (CONTACT_CHANGE_DOCUMENT_TYPES.includes(updatedDocument.documentType)) { if (servedParties.paper.length > 0) { const { Body: pdfData } = await applicationContext .getStorageClient() @@ -293,7 +295,7 @@ exports.completeDocketEntryQCInteractor = async ({ let noticeUpdatedDocument = new Document( { - ...Document.NOTICE_OF_DOCKET_CHANGE, + ...NOTICE_OF_DOCKET_CHANGE, documentId: noticeDocumentId, userId: user.userId, }, @@ -301,7 +303,7 @@ exports.completeDocketEntryQCInteractor = async ({ ); noticeUpdatedDocument.documentTitle = replaceBracketed( - Document.NOTICE_OF_DOCKET_CHANGE.documentTitle, + NOTICE_OF_DOCKET_CHANGE.documentTitle, docketChangeInfo.docketEntryIndex, ); @@ -323,6 +325,7 @@ exports.completeDocketEntryQCInteractor = async ({ ); const newPdfData = await addServedStampToDocument({ + applicationContext, pdfData, serviceStampText: `Served ${serviceStampDate}`, }); diff --git a/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.test.js b/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.test.js index 3fcecf94c17..bd86a6ad41b 100644 --- a/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.test.js +++ b/shared/src/business/useCases/editDocketEntry/completeDocketEntryQCInteractor.test.js @@ -6,7 +6,8 @@ const { const { completeDocketEntryQCInteractor, } = require('./completeDocketEntryQCInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); + const testAssetsPath = path.join(__dirname, '../../../../test-assets/'); describe('completeDocketEntryQCInteractor', () => { @@ -31,7 +32,8 @@ describe('completeDocketEntryQCInteractor', () => { }, isQC: true, section: 'docket', - sentBy: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + sentBy: 'Test User', + sentByUserId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', updatedAt: new Date().toISOString(), workItemId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; @@ -88,7 +90,7 @@ describe('completeDocketEntryQCInteractor', () => { partyType: 'Petitioner', preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; @@ -96,23 +98,20 @@ describe('completeDocketEntryQCInteractor', () => { compile: () => () => '', })); applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.docketClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.docketClerk, section: 'docket', userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); applicationContext .getPersistenceGateway() .getCaseByCaseId.mockReturnValue(caseRecord); - applicationContext - .getUseCaseHelpers() - .generatePaperServiceAddressPagePdf.mockReturnValue(testPdfDoc); applicationContext.getUniqueId.mockReturnValue( 'b6f835aa-bf95-4996-b858-c8e94566db47', ); diff --git a/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.js b/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.js index caffcb6c16c..2de6e0fed71 100644 --- a/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.js +++ b/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.js @@ -7,7 +7,7 @@ const { } = require('../../../authorization/authorizationClientService'); const { capitalize, pick } = require('lodash'); const { Case } = require('../../entities/cases/Case'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); const { DocketRecord } = require('../../entities/DocketRecord'); const { Document } = require('../../entities/Document'); const { Message } = require('../../entities/Message'); @@ -184,7 +184,8 @@ exports.fileExternalDocumentForConsolidatedInteractor = async ({ }, isQC: true, section: DOCKET_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, }, { applicationContext }, ); diff --git a/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.test.js b/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.test.js index 7cc483b8f5d..13510647d6e 100644 --- a/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.test.js +++ b/shared/src/business/useCases/externalDocument/fileExternalDocumentForConsolidatedInteractor.test.js @@ -4,9 +4,8 @@ const { const { fileExternalDocumentForConsolidatedInteractor, } = require('./fileExternalDocumentForConsolidatedInteractor'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); const { MOCK_CASE } = require('../../../test/mockCase.js'); -const { User } = require('../../entities/User'); +const { PARTY_TYPES, ROLES } = require('../../entities/EntityConstants'); describe('fileExternalDocumentForConsolidatedInteractor', () => { let caseRecords; @@ -41,10 +40,10 @@ describe('fileExternalDocumentForConsolidatedInteractor', () => { documents: MOCK_CASE.documents, filingType: 'Myself', leadCaseId: caseId0, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }, { @@ -67,10 +66,10 @@ describe('fileExternalDocumentForConsolidatedInteractor', () => { documents: MOCK_CASE.documents, filingType: 'Myself', leadCaseId: caseId0, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }, { @@ -93,10 +92,10 @@ describe('fileExternalDocumentForConsolidatedInteractor', () => { documents: MOCK_CASE.documents, filingType: 'Myself', leadCaseId: caseId0, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }, ]; diff --git a/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.js b/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.js index 705b542382b..1f3b46cca4d 100644 --- a/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.js +++ b/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.js @@ -7,7 +7,8 @@ const { } = require('../../../authorization/authorizationClientService'); const { capitalize, pick } = require('lodash'); const { Case } = require('../../entities/cases/Case'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); const { DocketRecord } = require('../../entities/DocketRecord'); const { Document } = require('../../entities/Document'); const { Message } = require('../../entities/Message'); @@ -127,7 +128,7 @@ exports.fileExternalDocumentInteractor = async ({ ); const highPriorityWorkItem = - caseEntity.status === Case.STATUS_TYPES.calendared; + caseEntity.status === CASE_STATUS_TYPES.calendared; const workItem = new WorkItem( { @@ -147,7 +148,8 @@ exports.fileExternalDocumentInteractor = async ({ highPriority: highPriorityWorkItem, isQC: true, section: DOCKET_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, trialDate: caseEntity.trialDate, }, { applicationContext }, @@ -186,7 +188,7 @@ exports.fileExternalDocumentInteractor = async ({ await applicationContext.getUseCaseHelpers().sendServedPartiesEmails({ applicationContext, - caseEntity: caseToUpdate, + caseEntity, documentEntity, servedParties, }); diff --git a/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.test.js b/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.test.js index 2e9c1815d93..afe4da5e82a 100644 --- a/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.test.js +++ b/shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor.test.js @@ -4,9 +4,10 @@ const { const { fileExternalDocumentInteractor, } = require('./fileExternalDocumentInteractor'); -const { Case } = require('../../entities/cases/Case'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); +const { AUTOMATIC_BLOCKED_REASONS } = require('../../entities/EntityConstants'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { MOCK_USERS } = require('../../../test/mockUsers'); +const { PARTY_TYPES, ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); describe('fileExternalDocumentInteractor', () => { @@ -45,35 +46,35 @@ describe('fileExternalDocumentInteractor', () => { documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: '15fac684-d333-45c2-b414-4af63a7f7613', }, { docketNumber: '45678-18', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: '15fac684-d333-45c2-b414-4af63a7f7613', }, { docketNumber: '45678-18', documentId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', documentType: 'Answer', eventCode: 'A', - userId: 'irsPractitioner', + userId: '15fac684-d333-45c2-b414-4af63a7f7613', }, ], filingType: 'Myself', - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Regular', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '0e97c6b4-d299-44f5-af99-2ce905d520f2', }; applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'irsPractitioner', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }), ); @@ -223,7 +224,7 @@ describe('fileExternalDocumentInteractor', () => { }); it('should create a high-priority work item if the case status is calendared', async () => { - caseRecord.status = Case.STATUS_TYPES.calendared; + caseRecord.status = CASE_STATUS_TYPES.calendared; caseRecord.trialDate = '2019-03-01T21:40:46.415Z'; await fileExternalDocumentInteractor({ @@ -250,7 +251,7 @@ describe('fileExternalDocumentInteractor', () => { }); it('should create a not-high-priority work item if the case status is not calendared', async () => { - caseRecord.status = Case.STATUS_TYPES.new; + caseRecord.status = CASE_STATUS_TYPES.new; await fileExternalDocumentInteractor({ applicationContext, @@ -295,7 +296,7 @@ describe('fileExternalDocumentInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pending, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pending, }); expect( applicationContext.getPersistenceGateway() @@ -331,7 +332,7 @@ describe('fileExternalDocumentInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pendingAndDueDate, }); expect( applicationContext.getPersistenceGateway() diff --git a/shared/src/business/useCases/externalDocument/uploadDocumentInteractor.test.js b/shared/src/business/useCases/externalDocument/uploadDocumentInteractor.test.js index 2e80fd8060e..5bb4fd6b924 100644 --- a/shared/src/business/useCases/externalDocument/uploadDocumentInteractor.test.js +++ b/shared/src/business/useCases/externalDocument/uploadDocumentInteractor.test.js @@ -1,8 +1,8 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { ROLES } = require('../../entities/EntityConstants'); const { uploadDocumentInteractor } = require('./uploadDocumentInteractor'); -const { User } = require('../../entities/User'); describe('uploadDocumentInteractor', () => { it('throws an error when an unauthorized user tries to access the use case', async () => { @@ -26,7 +26,7 @@ describe('uploadDocumentInteractor', () => { it('runs successfully with no errors with a valid user', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); @@ -46,7 +46,7 @@ describe('uploadDocumentInteractor', () => { it('runs successfully with no errors with all data and valid user', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); @@ -77,7 +77,7 @@ describe('uploadDocumentInteractor', () => { it('runs successfully with no errors with all data and valid user who is a docketclerk', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketclerk', }); diff --git a/shared/src/business/useCases/externalDocument/uploadExternalDocumentsInteractor.test.js b/shared/src/business/useCases/externalDocument/uploadExternalDocumentsInteractor.test.js index 95c33896ad4..b97ee98fd08 100644 --- a/shared/src/business/useCases/externalDocument/uploadExternalDocumentsInteractor.test.js +++ b/shared/src/business/useCases/externalDocument/uploadExternalDocumentsInteractor.test.js @@ -4,12 +4,12 @@ const { const { uploadExternalDocumentsInteractor, } = require('./uploadExternalDocumentsInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('uploadExternalDocumentsInteractor', () => { it('throws an error when an unauthorized user tries to access the use case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); @@ -28,7 +28,7 @@ describe('uploadExternalDocumentsInteractor', () => { it('runs successfully with no errors with minimum data and valid user', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'irsPractitioner', }); @@ -48,7 +48,7 @@ describe('uploadExternalDocumentsInteractor', () => { it('runs successfully with no errors with all data and valid user', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'irsPractitioner', }); @@ -79,7 +79,7 @@ describe('uploadExternalDocumentsInteractor', () => { it('runs successfully with no errors with all data and valid user who is a practitioner', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'irsPractitioner', }); diff --git a/shared/src/business/useCases/externalDocument/uploadOrderDocumentInteractor.test.js b/shared/src/business/useCases/externalDocument/uploadOrderDocumentInteractor.test.js index 44a9a94d8ae..7948d16f3aa 100644 --- a/shared/src/business/useCases/externalDocument/uploadOrderDocumentInteractor.test.js +++ b/shared/src/business/useCases/externalDocument/uploadOrderDocumentInteractor.test.js @@ -4,8 +4,8 @@ const { const { uploadOrderDocumentInteractor, } = require('./uploadOrderDocumentInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('uploadOrderDocumentInteractor', () => { it('throws an error when an unauthorized user tries to access the use case', async () => { @@ -20,7 +20,7 @@ describe('uploadOrderDocumentInteractor', () => { it('uploads documents on behalf of authorized users', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'admin', }); diff --git a/shared/src/business/useCases/filePetitionFromPaperInteractor.test.js b/shared/src/business/useCases/filePetitionFromPaperInteractor.test.js index c00c74b00aa..aca38ed8581 100644 --- a/shared/src/business/useCases/filePetitionFromPaperInteractor.test.js +++ b/shared/src/business/useCases/filePetitionFromPaperInteractor.test.js @@ -2,7 +2,7 @@ const { filePetitionFromPaperInteractor, } = require('./filePetitionFromPaperInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); beforeAll(() => { applicationContext @@ -30,7 +30,7 @@ describe('filePetitionFromPaperInteractor', () => { it('throws an error when an unauthorized user tries to access the case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'irsPractitioner', }); let error; @@ -48,7 +48,7 @@ describe('filePetitionFromPaperInteractor', () => { it('calls upload on a Petition file', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsClerk', }); await filePetitionFromPaperInteractor({ diff --git a/shared/src/business/useCases/filePetitionInteractor.test.js b/shared/src/business/useCases/filePetitionInteractor.test.js index 78874f06c76..caa1780179a 100644 --- a/shared/src/business/useCases/filePetitionInteractor.test.js +++ b/shared/src/business/useCases/filePetitionInteractor.test.js @@ -1,6 +1,6 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { filePetitionInteractor } = require('./filePetitionInteractor'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); beforeAll(() => { applicationContext @@ -26,7 +26,7 @@ describe('filePetitionInteractor', () => { it('throws an error when an unauthorized user tries to access the case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'irsPractitioner', }); let error; @@ -44,7 +44,7 @@ describe('filePetitionInteractor', () => { it('calls upload on a Petition file', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); await filePetitionInteractor({ diff --git a/shared/src/business/useCases/generateDocketRecordPdfInteractor.js b/shared/src/business/useCases/generateDocketRecordPdfInteractor.js index 91b6af9befb..0a4b9b5af50 100644 --- a/shared/src/business/useCases/generateDocketRecordPdfInteractor.js +++ b/shared/src/business/useCases/generateDocketRecordPdfInteractor.js @@ -45,7 +45,9 @@ exports.generateDocketRecordPdfInteractor = async ({ }, }); - return await applicationContext - .getUseCaseHelpers() - .saveFileAndGenerateUrl({ applicationContext, file: pdf }); + return await applicationContext.getUseCaseHelpers().saveFileAndGenerateUrl({ + applicationContext, + file: pdf, + useTempBucket: true, + }); }; diff --git a/shared/src/business/useCases/generateDocketRecordPdfInteractor.test.js b/shared/src/business/useCases/generateDocketRecordPdfInteractor.test.js index f67c46db52b..2a761518233 100644 --- a/shared/src/business/useCases/generateDocketRecordPdfInteractor.test.js +++ b/shared/src/business/useCases/generateDocketRecordPdfInteractor.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../entities/contacts/ContactFactory'; +import { COUNTRY_TYPES, PARTY_TYPES } from '../entities/EntityConstants'; import { generateDocketRecordPdfInteractor } from './generateDocketRecordPdfInteractor'; const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_USERS } = require('../../test/mockUsers'); @@ -11,7 +11,7 @@ const caseDetail = { contactPrimary: { address1: 'address 1', city: 'City', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, name: 'Test Petitioner', phone: '123-123-1234', postalCode: '12345', @@ -58,7 +58,7 @@ const caseDetail = { }, ], irsPractitioners: [], - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, privatePractitioners: [], }; diff --git a/shared/src/business/useCases/generatePDFFromJPGDataInteractor.js b/shared/src/business/useCases/generatePDFFromJPGDataInteractor.js index b7c74190a5f..c6382868af6 100644 --- a/shared/src/business/useCases/generatePDFFromJPGDataInteractor.js +++ b/shared/src/business/useCases/generatePDFFromJPGDataInteractor.js @@ -6,6 +6,6 @@ const { generatePDFFromJPGs } = require('../../utilities/generatePDFFromJPGs'); * @param imgData // Array of Uint8Array containing img data */ -exports.generatePDFFromJPGDataInteractor = imgData => { - return generatePDFFromJPGs(imgData); +exports.generatePDFFromJPGDataInteractor = (imgData, applicationContext) => { + return generatePDFFromJPGs(imgData, applicationContext); }; diff --git a/shared/src/business/useCases/generatePDFFromJPGDataInteractor.test.js b/shared/src/business/useCases/generatePDFFromJPGDataInteractor.test.js index b43bb00b158..2bca04807a5 100644 --- a/shared/src/business/useCases/generatePDFFromJPGDataInteractor.test.js +++ b/shared/src/business/useCases/generatePDFFromJPGDataInteractor.test.js @@ -5,6 +5,8 @@ const { } = require('./generatePDFFromJPGDataInteractor.js'); const { PDFDocument } = require('pdf-lib'); +const { applicationContext } = require('../test/createTestApplicationContext'); + const testAssetsPath = path.join(__dirname, '../../../test-assets/'); const testOutputPath = path.join(__dirname, '../../../test-output/'); @@ -22,7 +24,10 @@ describe('generatePDFFromJPGDataInteractor', () => { it('generates a pdf document from the provided imgData array', async () => { const imgData = [testJpg, testJpg]; - const newPdfData = await generatePDFFromJPGDataInteractor(imgData); + const newPdfData = await generatePDFFromJPGDataInteractor( + imgData, + applicationContext, + ); fs.writeFileSync( testOutputPath + 'generatePDFFromJPGDataInteractor.pdf', diff --git a/shared/src/business/useCases/generatePdfFromHtmlInteractor.js b/shared/src/business/useCases/generatePdfFromHtmlInteractor.js index 2ad4652af54..97d4063ee21 100644 --- a/shared/src/business/useCases/generatePdfFromHtmlInteractor.js +++ b/shared/src/business/useCases/generatePdfFromHtmlInteractor.js @@ -1,5 +1,6 @@ /** * generatePdfFromHtmlInteractor + * * @param {object} providers the providers object * @param {object} providers.applicationContext the application context * @param {string} providers.docketNumber the docket number of the case diff --git a/shared/src/business/useCases/generateSignedDocumentInteractor.js b/shared/src/business/useCases/generateSignedDocumentInteractor.js index cb79c1b7527..6c7ca5f875d 100644 --- a/shared/src/business/useCases/generateSignedDocumentInteractor.js +++ b/shared/src/business/useCases/generateSignedDocumentInteractor.js @@ -1,4 +1,4 @@ -const { PDFDocument, rgb, StandardFonts } = require('pdf-lib'); +// TODO: refactor here /** * @param {PDFPage} page the page to get dimensions for @@ -22,6 +22,7 @@ exports.getPageDimensions = page => { * @returns {ByteArray} PDF data after signature is added */ exports.generateSignedDocumentInteractor = async ({ + applicationContext, pageIndex, pdfData, posX, @@ -29,6 +30,12 @@ exports.generateSignedDocumentInteractor = async ({ scale = 1, sigTextData, }) => { + const { + PDFDocument, + rgb, + StandardFonts, + } = await applicationContext.getPdfLib(); + const pdfDoc = await PDFDocument.load(pdfData); const pages = pdfDoc.getPages(); const page = pages[pageIndex]; diff --git a/shared/src/business/useCases/generateSignedDocumentInteractor.test.js b/shared/src/business/useCases/generateSignedDocumentInteractor.test.js index 68ab91dcae1..12fad83558f 100644 --- a/shared/src/business/useCases/generateSignedDocumentInteractor.test.js +++ b/shared/src/business/useCases/generateSignedDocumentInteractor.test.js @@ -3,6 +3,7 @@ const path = require('path'); const { generateSignedDocumentInteractor, } = require('./generateSignedDocumentInteractor'); +const { applicationContext } = require('../test/createTestApplicationContext'); const { PDFDocument } = require('pdf-lib'); describe('generateSignedDocument', () => { @@ -22,6 +23,7 @@ describe('generateSignedDocument', () => { it('generates a pdf document with the provided signature text attached', async () => { const args = { + applicationContext, pageIndex: 0, pdfData: testDoc, posX: 200, @@ -47,6 +49,7 @@ describe('generateSignedDocument', () => { it('uses a default scale value of 1 if not provided in args', async () => { const args = { + applicationContext, pageIndex: 0, pdfData: testDoc, posX: 200, diff --git a/shared/src/business/useCases/getBlockedCasesInteractor.test.js b/shared/src/business/useCases/getBlockedCasesInteractor.test.js index 7f36d98d67c..beca3a2083f 100644 --- a/shared/src/business/useCases/getBlockedCasesInteractor.test.js +++ b/shared/src/business/useCases/getBlockedCasesInteractor.test.js @@ -1,11 +1,11 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { getBlockedCasesInteractor } = require('./getBlockedCasesInteractor'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('getBlockedCasesInteractor', () => { it('calls search function with correct params and returns records', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); @@ -35,7 +35,7 @@ describe('getBlockedCasesInteractor', () => { it('should throw an unauthorized error if the user does not have access to blocked cases', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); diff --git a/shared/src/business/useCases/getCaseInteractor.js b/shared/src/business/useCases/getCaseInteractor.js index 751180b106f..c7625f9da16 100644 --- a/shared/src/business/useCases/getCaseInteractor.js +++ b/shared/src/business/useCases/getCaseInteractor.js @@ -13,22 +13,30 @@ const getDocumentContentsForDocuments = async ({ }) => { for (const document of documents) { if (document.documentContentsId) { - const documentContentsFile = await applicationContext - .getPersistenceGateway() - .getDocument({ - applicationContext, - documentId: document.documentContentsId, - protocol: 'S3', - useTempBucket: true, - }); + try { + const documentContentsFile = await applicationContext + .getPersistenceGateway() + .getDocument({ + applicationContext, + documentId: document.documentContentsId, + protocol: 'S3', + useTempBucket: false, + }); - const documentContentsData = JSON.parse(documentContentsFile.toString()); - document.documentContents = documentContentsData.documentContents; - document.draftState = { - ...document.draftState, - documentContents: documentContentsData.documentContents, - richText: documentContentsData.richText, - }; + const documentContentsData = JSON.parse( + documentContentsFile.toString(), + ); + document.documentContents = documentContentsData.documentContents; + document.draftState = { + ...document.draftState, + documentContents: documentContentsData.documentContents, + richText: documentContentsData.richText, + }; + } catch (e) { + applicationContext.logger.error( + `Document contents ${document.documentContentsId} could not be found in the S3 bucket.`, + ); + } } } @@ -53,7 +61,7 @@ exports.getCaseInteractor = async ({ applicationContext, caseId }) => { applicationContext, caseId, }); - } else if (Case.isValidDocketNumber(caseId)) { + } else { caseRecord = await applicationContext .getPersistenceGateway() .getCaseByDocketNumber({ diff --git a/shared/src/business/useCases/getCaseInteractor.test.js b/shared/src/business/useCases/getCaseInteractor.test.js index 29519aa49be..80a26dfed9d 100644 --- a/shared/src/business/useCases/getCaseInteractor.test.js +++ b/shared/src/business/useCases/getCaseInteractor.test.js @@ -1,15 +1,20 @@ const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { getCaseInteractor } = require('./getCaseInteractor'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); const { documents } = MOCK_CASE; +const petitionsclerkId = '23c4d382-1136-492f-b1f4-45e893c34771'; +const petitionerId = '273f5d19-3707-41c0-bccc-449c52dfe54e'; +const irsPractitionerId = '6cf19fba-18c6-467a-9ea6-7a14e42add2f'; +const practitionerId = '295c3640-7ff9-40bb-b2f1-8117bba084ea'; +const practitioner2Id = '42614976-4228-49aa-a4c3-597dae1c7220'; + describe('Get case', () => { it('success case by case id', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: petitionsclerkId, }); applicationContext .getPersistenceGateway() @@ -37,14 +42,14 @@ describe('Get case', () => { draftState: {}, eventCode: 'P', processingStatus: 'pending', - userId: 'petitioner', + userId: petitionerId, workItems: [], }, ], }; applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: petitionsclerkId, }); applicationContext .getPersistenceGateway() @@ -63,7 +68,14 @@ describe('Get case', () => { caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); - expect(applicationContext.getPersistenceGateway().getDocument).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().getDocument, + ).toHaveBeenCalledWith({ + applicationContext, + documentId: '0098d177-78ef-4210-88aa-4bbb45c4f048', + protocol: 'S3', + useTempBucket: false, + }); expect(caseRecord.documents[0]).toMatchObject({ documentContents: 'the contents!', draftState: { @@ -75,8 +87,8 @@ describe('Get case', () => { it('failure case by case id', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: petitionsclerkId, }); applicationContext .getPersistenceGateway() @@ -95,8 +107,8 @@ describe('Get case', () => { it('success case by docket number', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: petitionsclerkId, }); applicationContext .getPersistenceGateway() @@ -115,6 +127,9 @@ describe('Get case', () => { }); it('failure case by docket number', async () => { + applicationContext + .getPersistenceGateway() + .getCaseByDocketNumber.mockResolvedValue(undefined); await expect( getCaseInteractor({ applicationContext, @@ -135,7 +150,7 @@ describe('Get case', () => { caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', caseType: 'Other', createdAt: new Date().toISOString(), - docketNumber: '00101-00', + docketNumber: '101-00', documents, petitioners: [{ name: 'Test Petitioner' }], preferredTrialCity: 'Washington, District of Columbia', @@ -166,12 +181,12 @@ describe('Get case', () => { docketNumber: '00101-18', documents, irsPractitioners: [ - { role: User.ROLES.irsPractitioner, userId: 'irsPractitioner' }, + { role: ROLES.irsPractitioner, userId: irsPractitionerId }, ], petitioners: [{ name: 'Test Petitioner' }], preferredTrialCity: 'Washington, District of Columbia', privatePractitioners: [ - { role: User.ROLES.privatePractitioner, userId: 'practitioner' }, + { role: ROLES.privatePractitioner, userId: practitionerId }, ], procedureType: 'Regular', sealedDate: new Date().toISOString(), @@ -181,7 +196,7 @@ describe('Get case', () => { it('restricted case by inadequate permissions', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'practitioner2', }); @@ -202,8 +217,8 @@ describe('Get case', () => { it('full case access via sealed case permissions', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, - userId: 'practitioner2', + role: ROLES.docketClerk, + userId: practitioner2Id, }); let error, result; @@ -227,8 +242,8 @@ describe('Get case', () => { it('throws an error if the entity returned from persistence is invalid', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: petitionsclerkId, }); applicationContext .getPersistenceGateway() @@ -237,7 +252,7 @@ describe('Get case', () => { caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', caseType: 'Other', createdAt: new Date().toISOString(), - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitioners: [{ name: 'Test Petitioner' }], preferredTrialCity: 'Washington, District of Columbia', procedureType: 'Regular', diff --git a/shared/src/business/useCases/getClosedCasesInteractor.js b/shared/src/business/useCases/getClosedCasesInteractor.js new file mode 100644 index 00000000000..d32832b1a8d --- /dev/null +++ b/shared/src/business/useCases/getClosedCasesInteractor.js @@ -0,0 +1,30 @@ +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); +const { UserCase } = require('../entities/UserCase'); + +/** + * getClosedCasesInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @returns {object} the closed cases data + */ +exports.getClosedCasesInteractor = async ({ applicationContext }) => { + let closedCases; + let foundCases = []; + + const { userId } = await applicationContext.getCurrentUser(); + + closedCases = await applicationContext + .getPersistenceGateway() + .getIndexedCasesForUser({ + applicationContext, + statuses: [CASE_STATUS_TYPES.closed], + userId, + }); + + foundCases = UserCase.validateRawCollection(closedCases, { + applicationContext, + }); + + return foundCases; +}; diff --git a/shared/src/business/useCases/getClosedCasesInteractor.test.js b/shared/src/business/useCases/getClosedCasesInteractor.test.js new file mode 100644 index 00000000000..f5b001b14e2 --- /dev/null +++ b/shared/src/business/useCases/getClosedCasesInteractor.test.js @@ -0,0 +1,85 @@ +const { applicationContext } = require('../test/createTestApplicationContext'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); +const { getClosedCasesInteractor } = require('./getClosedCasesInteractor'); +const { MOCK_CASE } = require('../../test/mockCase'); +const { MOCK_USERS } = require('../../test/mockUsers'); +jest.mock('../entities/UserCase'); +const { UserCase } = require('../entities/UserCase'); + +describe('getClosedCasesInteractor', () => { + let mockFoundCasesList; + + beforeEach(() => { + mockFoundCasesList = [MOCK_CASE]; + + applicationContext + .getPersistenceGateway() + .getUserById.mockReturnValue( + MOCK_USERS['d7d90c05-f6cd-442c-a168-202db587f16f'], + ); + applicationContext.getCurrentUser.mockReturnValue( + MOCK_USERS['d7d90c05-f6cd-442c-a168-202db587f16f'], + ); + applicationContext + .getPersistenceGateway() + .getIndexedCasesForUser.mockImplementation(() => mockFoundCasesList); + UserCase.validateRawCollection.mockImplementation( + foundCases => foundCases || [], + ); + }); + + it('should retrieve the current user information', async () => { + await getClosedCasesInteractor({ + applicationContext, + }); + + expect(applicationContext.getCurrentUser).toBeCalled(); + }); + + it('should make a call to retrieve closed cases by user', async () => { + await getClosedCasesInteractor({ + applicationContext, + }); + + expect( + applicationContext.getPersistenceGateway().getIndexedCasesForUser, + ).toHaveBeenCalledWith({ + applicationContext, + statuses: [CASE_STATUS_TYPES.closed], + userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', + }); + }); + + it('should return an empty list when no closed cases are found', async () => { + mockFoundCasesList = null; + + const result = await getClosedCasesInteractor({ + applicationContext, + }); + + expect(result).toEqual([]); + }); + + it('should validate the found closed cases', async () => { + await getClosedCasesInteractor({ + applicationContext, + }); + + expect(UserCase.validateRawCollection).toBeCalled(); + }); + + it('should return a list of closed cases', async () => { + const result = await getClosedCasesInteractor({ + applicationContext, + }); + + expect(result).toMatchObject([ + { + caseCaption: MOCK_CASE.caseCaption, + caseId: MOCK_CASE.caseId, + docketNumber: MOCK_CASE.docketNumber, + docketNumberWithSuffix: MOCK_CASE.docketNumberWithSuffix, + }, + ]); + }); +}); diff --git a/shared/src/business/useCases/getConsolidatedCasesByUserInteractor.js b/shared/src/business/useCases/getConsolidatedCasesByUserInteractor.js deleted file mode 100644 index f7274090985..00000000000 --- a/shared/src/business/useCases/getConsolidatedCasesByUserInteractor.js +++ /dev/null @@ -1,87 +0,0 @@ -const { Case } = require('../entities/cases/Case'); - -/** - * getConsolidatedCasesByUserInteractor - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {string} providers.userId id of the user to get cases for - * @returns {Array} the cases the user is associated with - */ -exports.getConsolidatedCasesByUserInteractor = async ({ - applicationContext, - userId, -}) => { - let foundCases = []; - let userCaseIdsMap = {}; - - const userCases = await applicationContext - .getPersistenceGateway() - .getCasesByUser({ applicationContext, userId }); - - const userCasesValidated = Case.validateRawCollection(userCases, { - applicationContext, - }); - - if (userCasesValidated.length) { - const caseMapping = {}; - const leadCaseIdsToGet = []; - - userCasesValidated.forEach(caseRecord => { - const { caseId, leadCaseId } = caseRecord; - - caseRecord.isRequestingUserAssociated = true; - userCaseIdsMap[caseId] = true; - - if (!leadCaseId || leadCaseId === caseId) { - caseMapping[caseId] = caseRecord; - } - - if (leadCaseId) { - if (leadCaseIdsToGet.indexOf(leadCaseId) === -1) { - leadCaseIdsToGet.push(leadCaseId); - } - } - }); - - for (const leadCaseId of leadCaseIdsToGet) { - const consolidatedCases = await applicationContext - .getPersistenceGateway() - .getCasesByLeadCaseId({ - applicationContext, - leadCaseId, - }); - - const consolidatedCasesValidated = Case.validateRawCollection( - consolidatedCases, - { applicationContext, filtered: true }, - ); - - if (!caseMapping[leadCaseId]) { - const leadCase = consolidatedCasesValidated.find( - consolidatedCase => consolidatedCase.caseId === leadCaseId, - ); - leadCase.isRequestingUserAssociated = false; - caseMapping[leadCaseId] = leadCase; - } - - const caseConsolidatedCases = []; - consolidatedCasesValidated.forEach(consolidatedCase => { - consolidatedCase.isRequestingUserAssociated = !!userCaseIdsMap[ - consolidatedCase.caseId - ]; - if (consolidatedCase.caseId !== leadCaseId) { - caseConsolidatedCases.push(consolidatedCase); - } - }); - - caseMapping[leadCaseId].consolidatedCases = Case.sortByDocketNumber( - caseConsolidatedCases, - ); - } - - foundCases = Object.keys(caseMapping).map(caseId => caseMapping[caseId]); - } - - return foundCases; -}; diff --git a/shared/src/business/useCases/getConsolidatedCasesByUserInteractor.test.js b/shared/src/business/useCases/getConsolidatedCasesByUserInteractor.test.js deleted file mode 100644 index 1e54490ef8b..00000000000 --- a/shared/src/business/useCases/getConsolidatedCasesByUserInteractor.test.js +++ /dev/null @@ -1,142 +0,0 @@ -const { - getConsolidatedCasesByUserInteractor, -} = require('./getConsolidatedCasesByUserInteractor'); -const { applicationContext } = require('../test/createTestApplicationContext'); -const { MOCK_CASE } = require('../../test/mockCase'); -const { MOCK_USERS } = require('../../test/mockUsers'); - -describe('getConsolidatedCasesByUserInteractor', () => { - beforeEach(() => {}); - - it('returns a collection of consolidated cases for the given user id', async () => { - applicationContext.getCurrentUser.mockReturnValue( - MOCK_USERS['a7d90c05-f6cd-442c-a168-202db587f16f'], - ); - applicationContext - .getPersistenceGateway() - .getCasesByUser.mockImplementation(({ userId }) => { - const casesByUserId = { - '74fa8ba9-4f05-45db-9e2d-260a306d0b5e': [ - { - ...MOCK_CASE, - caseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - { - ...MOCK_CASE, - caseId: '6ddedc7e-6947-4cfd-a143-833f6de24e95', - }, - { - ...MOCK_CASE, - caseId: '9a1ee699-90d7-4439-a6a8-f910d3441af4', - leadCaseId: 'f6a764ed-f826-41d4-8214-74e959b19ac1', - }, - ], - 'ab2dc429-9055-429f-a7a9-06d3d5324b97': [ - { - ...MOCK_CASE, - caseId: '8453c80c-b1d5-4975-899c-419ff323a506', - leadCaseId: '', - }, - ], - }; - - return casesByUserId[userId]; - }); - - applicationContext - .getPersistenceGateway() - .getCasesByLeadCaseId.mockImplementation(({ leadCaseId }) => { - const casesByLeadCaseId = { - 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee': [ - { - ...MOCK_CASE, - caseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - { - ...MOCK_CASE, - caseId: 'bd6d4823-92bc-4ea7-a3af-179a07dfda9e', - docketNumber: '234-02', - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - { - ...MOCK_CASE, - caseId: '6ddedc7e-6947-4cfd-a143-833f6de24e95', - docketNumber: '345-01', - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - { - ...MOCK_CASE, - caseId: '8453c80c-b1d5-4975-899c-419ff323a506', - docketNumber: '456-01', - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - ], - 'f6a764ed-f826-41d4-8214-74e959b19ac1': [ - { - ...MOCK_CASE, - caseId: 'f6a764ed-f826-41d4-8214-74e959b19ac1', - leadCaseId: 'f6a764ed-f826-41d4-8214-74e959b19ac1', - }, - { - ...MOCK_CASE, - caseId: '9a1ee699-90d7-4439-a6a8-f910d3441af4', - leadCaseId: 'f6a764ed-f826-41d4-8214-74e959b19ac1', - }, - ], - }; - return casesByLeadCaseId[leadCaseId]; - }); - - const cases = await getConsolidatedCasesByUserInteractor({ - applicationContext, - userId: '74fa8ba9-4f05-45db-9e2d-260a306d0b5e', - }); - - expect(cases.length).toBeGreaterThan(0); - expect(cases).toMatchObject([ - { - caseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - consolidatedCases: [ - { - caseId: '6ddedc7e-6947-4cfd-a143-833f6de24e95', - docketNumber: '345-01', - isRequestingUserAssociated: true, - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - { - caseId: '8453c80c-b1d5-4975-899c-419ff323a506', - docketNumber: '456-01', - isRequestingUserAssociated: false, - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - { - caseId: 'bd6d4823-92bc-4ea7-a3af-179a07dfda9e', - docketNumber: '234-02', - isRequestingUserAssociated: false, - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - ], - isRequestingUserAssociated: true, - leadCaseId: 'e1f7668e-4504-4f33-8c5a-d4dc17f009ee', - }, - { - caseId: '6ddedc7e-6947-4cfd-a143-833f6de24e95', - isRequestingUserAssociated: true, - }, - { - caseId: 'f6a764ed-f826-41d4-8214-74e959b19ac1', - consolidatedCases: [ - { - caseId: '9a1ee699-90d7-4439-a6a8-f910d3441af4', - isRequestingUserAssociated: true, - leadCaseId: 'f6a764ed-f826-41d4-8214-74e959b19ac1', - }, - ], - isRequestingUserAssociated: false, - leadCaseId: 'f6a764ed-f826-41d4-8214-74e959b19ac1', - }, - ]); - }); -}); diff --git a/shared/src/business/useCases/getDownloadPolicyUrlInteractor.js b/shared/src/business/useCases/getDownloadPolicyUrlInteractor.js index d3de94d954b..af4b8697f13 100644 --- a/shared/src/business/useCases/getDownloadPolicyUrlInteractor.js +++ b/shared/src/business/useCases/getDownloadPolicyUrlInteractor.js @@ -6,6 +6,7 @@ const { ROLE_PERMISSIONS, } = require('../../authorization/authorizationClientService'); const { Case } = require('../entities/cases/Case'); +const { ROLES } = require('../entities/EntityConstants'); const { UnauthorizedError } = require('../../errors/errors'); const { User } = require('../entities/User'); @@ -27,8 +28,7 @@ exports.getDownloadPolicyUrlInteractor = async ({ } const isInternalUser = User.isInternalUser(user && user.role); - const isIrsSuperuser = - user && user.role && user.role === User.ROLES.irsSuperuser; + const isIrsSuperuser = user && user.role && user.role === ROLES.irsSuperuser; if (!isInternalUser && !isIrsSuperuser) { //verify that the user has access to this document diff --git a/shared/src/business/useCases/getDownloadPolicyUrlInteractor.test.js b/shared/src/business/useCases/getDownloadPolicyUrlInteractor.test.js index 142881e79b3..76f2624a477 100644 --- a/shared/src/business/useCases/getDownloadPolicyUrlInteractor.test.js +++ b/shared/src/business/useCases/getDownloadPolicyUrlInteractor.test.js @@ -4,7 +4,7 @@ const { const { applicationContext } = require('../test/createTestApplicationContext'); const { cloneDeep } = require('lodash'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('getDownloadPolicyUrlInteractor', () => { beforeEach(() => { @@ -33,7 +33,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('throw unauthorized error if user is not associated with case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext @@ -51,7 +51,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('throw unauthorized error if user is associated with the case but the document is not available for viewing at this time', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext @@ -86,7 +86,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('returns the expected policy url for a petitioner who is associated with the case and viewing an available document', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext @@ -103,7 +103,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('returns the expected policy url for a petitioner who is associated with the case and viewing a case confirmation pdf', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext @@ -120,7 +120,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('throws an Unauthorized error for a petitioner attempting to access an case confirmation pdf for a different case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext @@ -138,7 +138,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('returns the url for an internal user role even if verifyCaseForUser returns false', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsClerk', }); applicationContext @@ -155,7 +155,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('throws an error if the user role is irsSuperuser and the petition document on the case is not served', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: 'irsSuperuser', }); @@ -179,7 +179,7 @@ describe('getDownloadPolicyUrlInteractor', () => { it('returns the url if the user role is irsSuperuser and the petition document on the case is served', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: 'irsSuperuser', }); diff --git a/shared/src/business/useCases/getOpenConsolidatedCasesInteractor.js b/shared/src/business/useCases/getOpenConsolidatedCasesInteractor.js new file mode 100644 index 00000000000..073dfc8f375 --- /dev/null +++ b/shared/src/business/useCases/getOpenConsolidatedCasesInteractor.js @@ -0,0 +1,66 @@ +const { UserCase } = require('../entities/UserCase'); + +/** + * getOpenConsolidatedCasesInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @returns {object} the open cases data + */ +exports.getOpenConsolidatedCasesInteractor = async ({ applicationContext }) => { + const { userId } = await applicationContext.getCurrentUser(); + + let openUserCases = await applicationContext + .getPersistenceGateway() + .getIndexedCasesForUser({ + applicationContext, + statuses: applicationContext.getConstants().OPEN_CASE_STATUSES, + userId, + }); + + openUserCases = UserCase.validateRawCollection(openUserCases, { + applicationContext, + }); + + if (!openUserCases.length) { + return []; + } + + let { + casesAssociatedWithUserOrLeadCaseMap, + leadCaseIdsAssociatedWithUser, + userAssociatedCaseIdsMap, + } = applicationContext + .getUseCaseHelpers() + .processUserAssociatedCases(openUserCases); + + for (const leadCaseId of leadCaseIdsAssociatedWithUser) { + const consolidatedCases = await applicationContext + .getUseCaseHelpers() + .getConsolidatedCasesForLeadCase({ applicationContext, leadCaseId }); + + if (!casesAssociatedWithUserOrLeadCaseMap[leadCaseId]) { + casesAssociatedWithUserOrLeadCaseMap[ + leadCaseId + ] = applicationContext.getUseCaseHelpers().getUnassociatedLeadCase({ + casesAssociatedWithUserOrLeadCaseMap, + consolidatedCases, + leadCaseId, + }); + } + + casesAssociatedWithUserOrLeadCaseMap[ + leadCaseId + ].consolidatedCases = applicationContext + .getUseCaseHelpers() + .formatAndSortConsolidatedCases({ + consolidatedCases, + leadCaseId, + userAssociatedCaseIdsMap, + }); + } + + const foundCases = Object.values(casesAssociatedWithUserOrLeadCaseMap); + + return foundCases; +}; diff --git a/shared/src/business/useCases/getOpenConsolidatedCasesInteractor.test.js b/shared/src/business/useCases/getOpenConsolidatedCasesInteractor.test.js new file mode 100644 index 00000000000..e81abb6238c --- /dev/null +++ b/shared/src/business/useCases/getOpenConsolidatedCasesInteractor.test.js @@ -0,0 +1,134 @@ +const { + getOpenConsolidatedCasesInteractor, +} = require('./getOpenConsolidatedCasesInteractor'); +const { applicationContext } = require('../test/createTestApplicationContext'); +const { MOCK_CASE } = require('../../test/mockCase'); +const { MOCK_USERS } = require('../../test/mockUsers'); +jest.mock('../entities/UserCase'); +const { UserCase } = require('../entities/UserCase'); + +describe('getOpenConsolidatedCasesInteractor', () => { + let mockFoundCasesList; + + beforeEach(() => { + mockFoundCasesList = [MOCK_CASE]; + + applicationContext + .getPersistenceGateway() + .getUserById.mockReturnValue( + MOCK_USERS['d7d90c05-f6cd-442c-a168-202db587f16f'], + ); + applicationContext.getCurrentUser.mockReturnValue( + MOCK_USERS['d7d90c05-f6cd-442c-a168-202db587f16f'], + ); + applicationContext + .getPersistenceGateway() + .getIndexedCasesForUser.mockImplementation(() => mockFoundCasesList); + applicationContext + .getUseCaseHelpers() + .processUserAssociatedCases.mockReturnValue({ + casesAssociatedWithUserOrLeadCaseMap: { + 'c54ba5a9-b37b-479d-9201-067ec6e335bb': MOCK_CASE, + }, + leadCaseIdsAssociatedWithUser: [MOCK_CASE.caseId], + userAssociatedCaseIdsMap: {}, + }); + applicationContext + .getUseCaseHelpers() + .getConsolidatedCasesForLeadCase.mockReturnValue([]); + UserCase.validateRawCollection.mockImplementation(foundCases => foundCases); + }); + + it('should retrieve the current user information', async () => { + await getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + + expect(applicationContext.getCurrentUser).toBeCalled(); + }); + + it('should make a call to retrieve open cases by user', async () => { + await getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + + expect( + applicationContext.getPersistenceGateway().getIndexedCasesForUser, + ).toHaveBeenCalledWith({ + applicationContext, + statuses: applicationContext.getConstants().OPEN_CASE_STATUSES, + userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', + }); + }); + + it('should validate the list of found open cases', async () => { + await getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + + expect(UserCase.validateRawCollection).toBeCalled(); + }); + + it('should return an empty list when no open cases are found', async () => { + mockFoundCasesList = []; + + const result = await getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + + expect(result).toEqual([]); + }); + + it('should return a list of open cases', async () => { + const result = await getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + + expect(result).toMatchObject([ + { + caseCaption: MOCK_CASE.caseCaption, + caseId: MOCK_CASE.caseId, + docketNumber: MOCK_CASE.docketNumber, + docketNumberWithSuffix: MOCK_CASE.docketNumberWithSuffix, + }, + ]); + }); + + it('should return a list of open cases when the user is associated with a consolidated case that is not the lead case', async () => { + const consolidatedCaseThatIsNotTheLeadCase = { + ...MOCK_CASE, + caseId: applicationContext.getUniqueId(), + isLeadCase: false, + }; + const mockUserAssociatedCaseIdsMap = {}; + mockUserAssociatedCaseIdsMap[ + consolidatedCaseThatIsNotTheLeadCase.caseId + ] = true; + applicationContext + .getUseCaseHelpers() + .processUserAssociatedCases.mockReturnValue({ + casesAssociatedWithUserOrLeadCaseMap: {}, + leadCaseIdsAssociatedWithUser: [ + consolidatedCaseThatIsNotTheLeadCase.caseId, + ], + userAssociatedCaseIdsMap: mockUserAssociatedCaseIdsMap, + }); + applicationContext + .getUseCaseHelpers() + .getUnassociatedLeadCase.mockReturnValue(MOCK_CASE); + applicationContext + .getUseCaseHelpers() + .formatAndSortConsolidatedCases.mockReturnValue([ + consolidatedCaseThatIsNotTheLeadCase, + ]); + + const result = await getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + + expect(result[0]).toBe(MOCK_CASE); + expect(result[0].consolidatedCases[0]).toBe( + consolidatedCaseThatIsNotTheLeadCase, + ); + }); +}); diff --git a/shared/src/business/useCases/getUploadPolicyInteractor.test.js b/shared/src/business/useCases/getUploadPolicyInteractor.test.js index ced6a01e07a..dbe3875d3ec 100644 --- a/shared/src/business/useCases/getUploadPolicyInteractor.test.js +++ b/shared/src/business/useCases/getUploadPolicyInteractor.test.js @@ -1,6 +1,6 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { getUploadPolicyInteractor } = require('./getUploadPolicyInteractor'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('getUploadPolicyInteractor', () => { it('throw unauthorized error on invalid role', async () => { @@ -22,7 +22,7 @@ describe('getUploadPolicyInteractor', () => { it('returns the expected policy when the file does not already exist', async () => { applicationContext.getCurrentUser.mockReturnValue({ isExternalUser: () => true, - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext @@ -41,7 +41,7 @@ describe('getUploadPolicyInteractor', () => { it('throws an unauthorized exception when file already exists', async () => { applicationContext.getCurrentUser.mockReturnValue({ isExternalUser: () => true, - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext diff --git a/shared/src/business/useCases/getUserByIdInteractor.js b/shared/src/business/useCases/getUserByIdInteractor.js index 2666fa581fb..bda125fb4c0 100644 --- a/shared/src/business/useCases/getUserByIdInteractor.js +++ b/shared/src/business/useCases/getUserByIdInteractor.js @@ -2,6 +2,7 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../authorization/authorizationClientService'); +const { ROLES } = require('../entities/EntityConstants'); const { UnauthorizedError } = require('../../errors/errors'); const { User } = require('../entities/User'); @@ -22,11 +23,7 @@ exports.getUserByIdInteractor = async ({ applicationContext, userId }) => { .getPersistenceGateway() .getUserById({ applicationContext, userId }); - if ( - ![User.ROLES.privatePractitioner, User.ROLES.irsPractitioner].includes( - user.role, - ) - ) { + if (![ROLES.privatePractitioner, ROLES.irsPractitioner].includes(user.role)) { throw new UnauthorizedError( 'Unauthorized to retrieve users other than practitioners', ); diff --git a/shared/src/business/useCases/getUserByIdInteractor.test.js b/shared/src/business/useCases/getUserByIdInteractor.test.js index 5971f338381..5a9936b0c08 100644 --- a/shared/src/business/useCases/getUserByIdInteractor.test.js +++ b/shared/src/business/useCases/getUserByIdInteractor.test.js @@ -1,16 +1,17 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { getUserByIdInteractor } = require('./getUserByIdInteractor'); +const { ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); const MOCK_REQUEST_USER = new User({ name: 'Test Petitions clerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); const MOCK_RETRIEVED_USER = { name: 'Test Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', userId: '4f67802c-1948-4749-b070-38f7316b15c5', }; @@ -20,7 +21,7 @@ describe('getUserByIdInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'e93ce149-b42b-4764-802f-5d321ba36950', }), ); @@ -38,7 +39,7 @@ describe('getUserByIdInteractor', () => { applicationContext.getPersistenceGateway().getUserById.mockReturnValue( new User({ name: 'Test Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'e93ce149-b42b-4764-802f-5d321ba36950', }), ); @@ -69,7 +70,7 @@ describe('getUserByIdInteractor', () => { email: undefined, entityName: 'User', name: 'Test Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', token: undefined, userId: '4f67802c-1948-4749-b070-38f7316b15c5', diff --git a/shared/src/business/useCases/getUserInteractor.test.js b/shared/src/business/useCases/getUserInteractor.test.js index 7a630be8eb3..5199030ccb0 100644 --- a/shared/src/business/useCases/getUserInteractor.test.js +++ b/shared/src/business/useCases/getUserInteractor.test.js @@ -1,5 +1,6 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { getUserInteractor } = require('./getUserInteractor'); +const { ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('getUserInteractor', () => { @@ -7,13 +8,13 @@ describe('getUserInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -28,7 +29,7 @@ describe('getUserInteractor', () => { email: undefined, entityName: 'User', name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', token: undefined, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', @@ -39,13 +40,13 @@ describe('getUserInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Judge', - role: User.ROLES.judge, + role: ROLES.judge, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); applicationContext.getPersistenceGateway().getUserById.mockReturnValue({ name: 'Test Judge', - role: User.ROLES.judge, + role: ROLES.judge, section: 'judge', userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -60,7 +61,7 @@ describe('getUserInteractor', () => { email: undefined, entityName: 'User', name: 'Test Judge', - role: User.ROLES.judge, + role: ROLES.judge, section: 'judge', token: undefined, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', diff --git a/shared/src/business/useCases/loadPDFForSigningInteractor.js b/shared/src/business/useCases/loadPDFForSigningInteractor.js index 8ba16387bb0..d133834cf23 100644 --- a/shared/src/business/useCases/loadPDFForSigningInteractor.js +++ b/shared/src/business/useCases/loadPDFForSigningInteractor.js @@ -1,5 +1,3 @@ -const { PDFDocument } = require('pdf-lib'); - /** * loadPDFForSigningInteractor * @@ -17,6 +15,8 @@ exports.loadPDFForSigningInteractor = async ({ documentId, removeCover = false, }) => { + const { PDFDocument } = await applicationContext.getPdfLib(); + try { const pdfjsLib = await applicationContext.getPdfJs(); let pdfData = await applicationContext.getPersistenceGateway().getDocument({ diff --git a/shared/src/business/useCases/manualAssociation/associateIrsPractitionerWithCaseInteractor.test.js b/shared/src/business/useCases/manualAssociation/associateIrsPractitionerWithCaseInteractor.test.js index 98e3b7818d9..be5a9f5047d 100644 --- a/shared/src/business/useCases/manualAssociation/associateIrsPractitionerWithCaseInteractor.test.js +++ b/shared/src/business/useCases/manualAssociation/associateIrsPractitionerWithCaseInteractor.test.js @@ -4,11 +4,9 @@ const { const { associateIrsPractitionerWithCaseInteractor, } = require('./associateIrsPractitionerWithCaseInteractor'); -const { - SERVICE_INDICATOR_TYPES, -} = require('../../entities/cases/CaseConstants'); const { MOCK_CASE } = require('../../../test/mockCase.js'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); +const { SERVICE_INDICATOR_TYPES } = require('../../entities/EntityConstants'); describe('associateIrsPractitionerWithCaseInteractor', () => { let caseRecord = { @@ -64,13 +62,13 @@ describe('associateIrsPractitionerWithCaseInteractor', () => { it('should add mapping for an irsPractitioner', async () => { mockCurrentUser = { - name: 'Olivia Jade', - role: User.ROLES.adc, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.adc, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; mockUserById = { - name: 'Olivia Jade', - role: User.ROLES.irsPractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.irsPractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; applicationContext diff --git a/shared/src/business/useCases/manualAssociation/associatePrivatePractitionerWithCaseInteractor.test.js b/shared/src/business/useCases/manualAssociation/associatePrivatePractitionerWithCaseInteractor.test.js index f7d92d2fda2..ecb2aedc40a 100644 --- a/shared/src/business/useCases/manualAssociation/associatePrivatePractitionerWithCaseInteractor.test.js +++ b/shared/src/business/useCases/manualAssociation/associatePrivatePractitionerWithCaseInteractor.test.js @@ -4,7 +4,7 @@ const { const { associatePrivatePractitionerWithCaseInteractor, } = require('./associatePrivatePractitionerWithCaseInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('associatePrivatePractitionerWithCaseInteractor', () => { let caseRecord = { @@ -38,7 +38,7 @@ describe('associatePrivatePractitionerWithCaseInteractor', () => { documentTitle: 'Petition', documentType: 'Petition', processingStatus: 'pending', - userId: 'petitioner', + userId: 'd13d017b-28d1-45b6-aa7d-f54865b0121b', }, ], filingType: 'Myself', @@ -59,16 +59,16 @@ describe('associatePrivatePractitionerWithCaseInteractor', () => { it('should add mapping for a practitioner', async () => { applicationContext.getCurrentUser.mockReturnValue({ - name: 'Olivia Jade', - role: User.ROLES.adc, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.adc, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }); applicationContext .getPersistenceGateway() .getUserById.mockImplementation(() => { return { - name: 'Olivia Jade', - role: User.ROLES.privatePractitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.privatePractitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; }); diff --git a/shared/src/business/useCases/messages/createCaseMessageInteractor.js b/shared/src/business/useCases/messages/createCaseMessageInteractor.js new file mode 100644 index 00000000000..2a41ddbc885 --- /dev/null +++ b/shared/src/business/useCases/messages/createCaseMessageInteractor.js @@ -0,0 +1,80 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { CaseMessage } = require('../../entities/CaseMessage'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * creates a message on a case + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {array} providers.attachments array of objects containing documentId and documentTitle + * @param {string} providers.caseId the id of the case + * @param {string} providers.message the message text + * @param {string} providers.subject the message subject + * @param {string} providers.toSection the section of the user receiving the message + * @param {string} providers.toUserId the user id of the user receiving the message + * @returns {object} the created message + */ +exports.createCaseMessageInteractor = async ({ + applicationContext, + attachments, + caseId, + message, + subject, + toSection, + toUserId, +}) => { + const authorizedUser = applicationContext.getCurrentUser(); + + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.MESSAGES)) { + throw new UnauthorizedError('Unauthorized'); + } + + // TODO: Would it be better to just pass this in since case detail is already loaded in the action? + const { + docketNumber, + docketNumberWithSuffix, + status, + } = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ applicationContext, caseId }); + + const fromUser = await applicationContext + .getPersistenceGateway() + .getUserById({ applicationContext, userId: authorizedUser.userId }); + + const toUser = await applicationContext + .getPersistenceGateway() + .getUserById({ applicationContext, userId: toUserId }); + + const caseMessage = new CaseMessage( + { + attachments, + caseId, + caseStatus: status, + docketNumber, + docketNumberWithSuffix, + from: fromUser.name, + fromSection: fromUser.section, + fromUserId: fromUser.userId, + message, + subject, + to: toUser.name, + toSection, + toUserId, + }, + { applicationContext }, + ) + .validate() + .toRawObject(); + + await applicationContext.getPersistenceGateway().createCaseMessage({ + applicationContext, + caseMessage, + }); + + return caseMessage; +}; diff --git a/shared/src/business/useCases/messages/createCaseMessageInteractor.test.js b/shared/src/business/useCases/messages/createCaseMessageInteractor.test.js new file mode 100644 index 00000000000..bded6f3bd14 --- /dev/null +++ b/shared/src/business/useCases/messages/createCaseMessageInteractor.test.js @@ -0,0 +1,87 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + createCaseMessageInteractor, +} = require('./createCaseMessageInteractor'); +const { + UnauthorizedError, +} = require('../../../../../shared/src/errors/errors'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('createCaseMessageInteractor', () => { + it('throws unauthorized for a user without MESSAGES permission', async () => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitioner, + userId: '9bd0308c-2b06-4589-b36e-242398bea31b', + }); + + await expect( + createCaseMessageInteractor({ + applicationContext, + }), + ).rejects.toThrow(UnauthorizedError); + }); + + it('creates the case message', async () => { + const caseMessageData = { + attachments: [ + { + documentId: 'b1130321-0a76-43bc-b3eb-64a18f079873', + documentTitle: 'Petition', + }, + ], + caseId: '7a130321-0a76-43bc-b3eb-64a18f07987d', + message: "How's it going?", + subject: 'Hey!', + toSection: 'petitions', + toUserId: 'b427ca37-0df1-48ac-94bb-47aed073d6f7', + }; + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitionsClerk, + userId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + }); + applicationContext + .getPersistenceGateway() + .getUserById.mockReturnValueOnce({ + name: 'Test Petitionsclerk', + role: ROLES.petitionsClerk, + section: 'petitions', + userId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + }) + .mockReturnValueOnce({ + name: 'Test Petitionsclerk2', + role: ROLES.petitionsClerk, + section: 'petitions', + userId: 'd90c8a79-9628-4ca9-97c6-02a161a02904', + }); + + applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + status: 'General Docket - Not at Issue', + }); + + await createCaseMessageInteractor({ + applicationContext, + ...caseMessageData, + }); + + expect( + applicationContext.getPersistenceGateway().createCaseMessage, + ).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().createCaseMessage.mock + .calls[0][0].caseMessage, + ).toMatchObject({ + ...caseMessageData, + caseStatus: 'General Docket - Not at Issue', + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + from: 'Test Petitionsclerk', + fromSection: 'petitions', + fromUserId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + to: 'Test Petitionsclerk2', + }); + }); +}); diff --git a/shared/src/business/useCases/messages/getCaseMessageInteractor.js b/shared/src/business/useCases/messages/getCaseMessageInteractor.js new file mode 100644 index 00000000000..cdd549358e6 --- /dev/null +++ b/shared/src/business/useCases/messages/getCaseMessageInteractor.js @@ -0,0 +1,36 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { CaseMessage } = require('../../entities/CaseMessage'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * gets a case message + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.messageId the id of the message to retrieve + * @returns {object} the case message + */ +exports.getCaseMessageInteractor = async ({ + applicationContext, + messageId, +}) => { + const authorizedUser = applicationContext.getCurrentUser(); + + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.MESSAGES)) { + throw new UnauthorizedError('Unauthorized'); + } + + const caseMessage = await applicationContext + .getPersistenceGateway() + .getCaseMessageById({ + applicationContext, + messageId, + }); + + return new CaseMessage(caseMessage, { applicationContext }) + .validate() + .toRawObject(); +}; diff --git a/shared/src/business/useCases/messages/getCaseMessageInteractor.test.js b/shared/src/business/useCases/messages/getCaseMessageInteractor.test.js new file mode 100644 index 00000000000..e9046be250d --- /dev/null +++ b/shared/src/business/useCases/messages/getCaseMessageInteractor.test.js @@ -0,0 +1,60 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + UnauthorizedError, +} = require('../../../../../shared/src/errors/errors'); +const { getCaseMessageInteractor } = require('./getCaseMessageInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('getCaseMessageInteractor', () => { + it('throws unauthorized for a user without MESSAGES permission', async () => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitioner, + userId: '9bd0308c-2b06-4589-b36e-242398bea31b', + }); + + await expect( + getCaseMessageInteractor({ + applicationContext, + }), + ).rejects.toThrow(UnauthorizedError); + }); + + it('retrieves the case message from persistence and returns it', async () => { + const caseMessageData = { + caseId: '7a130321-0a76-43bc-b3eb-64a18f07987d', + caseStatus: 'General Docket - Not at Issue', + createdAt: '2019-03-01T21:40:46.415Z', + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + entityName: 'CaseMessage', + from: 'Test Petitionsclerk2', + fromSection: 'petitions', + fromUserId: 'fe6eeadd-e4e8-4e56-9ddf-0ebe9516df6b', + message: "How's it going?", + messageId: '9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + subject: 'Hey!', + to: 'Test Petitionsclerk', + toSection: 'petitions', + toUserId: 'b427ca37-0df1-48ac-94bb-47aed073d6f7', + }; + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitionsClerk, + userId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + }); + applicationContext + .getPersistenceGateway() + .getCaseMessageById.mockReturnValue(caseMessageData); + + const returnedMessage = await getCaseMessageInteractor({ + applicationContext, + messageId: caseMessageData.messageId, + }); + + expect( + applicationContext.getPersistenceGateway().getCaseMessageById, + ).toBeCalled(); + expect(returnedMessage).toEqual(caseMessageData); + }); +}); diff --git a/shared/src/business/useCases/messages/getInboxCaseMessagesForSectionInteractor.js b/shared/src/business/useCases/messages/getInboxCaseMessagesForSectionInteractor.js new file mode 100644 index 00000000000..54b42eb479e --- /dev/null +++ b/shared/src/business/useCases/messages/getInboxCaseMessagesForSectionInteractor.js @@ -0,0 +1,36 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { CaseMessage } = require('../../entities/CaseMessage'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * getInboxCaseMessagesForSectionInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.section the section to get the inbox messages + * @returns {object} the messages in the section inbox + */ +exports.getInboxCaseMessagesForSectionInteractor = async ({ + applicationContext, + section, +}) => { + const authorizedUser = applicationContext.getCurrentUser(); + + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.MESSAGES)) { + throw new UnauthorizedError('Unauthorized'); + } + + const messages = await applicationContext + .getPersistenceGateway() + .getSectionInboxMessages({ + applicationContext, + section, + }); + + return CaseMessage.validateRawCollection(messages, { + applicationContext, + }); +}; diff --git a/shared/src/business/useCases/messages/getInboxCaseMessagesForSectionInteractor.test.js b/shared/src/business/useCases/messages/getInboxCaseMessagesForSectionInteractor.test.js new file mode 100644 index 00000000000..6273ca5d284 --- /dev/null +++ b/shared/src/business/useCases/messages/getInboxCaseMessagesForSectionInteractor.test.js @@ -0,0 +1,64 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + getInboxCaseMessagesForSectionInteractor, +} = require('./getInboxCaseMessagesForSectionInteractor'); +const { + UnauthorizedError, +} = require('../../../../../shared/src/errors/errors'); +const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('getInboxCaseMessagesForSectionInteractor', () => { + it('throws unauthorized for a user without MESSAGES permission', async () => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitioner, + userId: '9bd0308c-2b06-4589-b36e-242398bea31b', + }); + + await expect( + getInboxCaseMessagesForSectionInteractor({ + applicationContext, + }), + ).rejects.toThrow(UnauthorizedError); + }); + + it('retrieves the case messages from persistence and returns them', async () => { + const caseMessageData = { + caseId: '7a130321-0a76-43bc-b3eb-64a18f07987d', + createdAt: '2019-03-01T21:40:46.415Z', + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + entityName: 'CaseMessage', + from: 'Test Petitionsclerk2', + fromSection: 'petitions', + fromUserId: 'fe6eeadd-e4e8-4e56-9ddf-0ebe9516df6b', + message: "How's it going?", + messageId: '9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + pk: 'case|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + sk: 'message|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + subject: 'Hey!', + to: 'Test Petitionsclerk', + toSection: 'petitions', + toUserId: 'b427ca37-0df1-48ac-94bb-47aed073d6f7', + }; + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitionsClerk, + userId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + }); + applicationContext + .getPersistenceGateway() + .getSectionInboxMessages.mockReturnValue([caseMessageData]); + + const returnedMessages = await getInboxCaseMessagesForSectionInteractor({ + applicationContext, + section: caseMessageData.section, + }); + + expect( + applicationContext.getPersistenceGateway().getSectionInboxMessages, + ).toBeCalled(); + expect(returnedMessages).toEqual([omit(caseMessageData, 'pk', 'sk')]); + }); +}); diff --git a/shared/src/business/useCases/messages/getInboxCaseMessagesForUserInteractor.js b/shared/src/business/useCases/messages/getInboxCaseMessagesForUserInteractor.js new file mode 100644 index 00000000000..dd9e00d9728 --- /dev/null +++ b/shared/src/business/useCases/messages/getInboxCaseMessagesForUserInteractor.js @@ -0,0 +1,36 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { CaseMessage } = require('../../entities/CaseMessage'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * getInboxCaseMessagesForUserInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.userId the user to get the inbox messages + * @returns {object} the messages in the user inbox + */ +exports.getInboxCaseMessagesForUserInteractor = async ({ + applicationContext, + userId, +}) => { + const authorizedUser = applicationContext.getCurrentUser(); + + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.MESSAGES)) { + throw new UnauthorizedError('Unauthorized'); + } + + const messages = await applicationContext + .getPersistenceGateway() + .getUserInboxMessages({ + applicationContext, + userId, + }); + + return CaseMessage.validateRawCollection(messages, { + applicationContext, + }); +}; diff --git a/shared/src/business/useCases/messages/getInboxCaseMessagesForUserInteractor.test.js b/shared/src/business/useCases/messages/getInboxCaseMessagesForUserInteractor.test.js new file mode 100644 index 00000000000..f18e5a6d943 --- /dev/null +++ b/shared/src/business/useCases/messages/getInboxCaseMessagesForUserInteractor.test.js @@ -0,0 +1,64 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + getInboxCaseMessagesForUserInteractor, +} = require('./getInboxCaseMessagesForUserInteractor'); +const { + UnauthorizedError, +} = require('../../../../../shared/src/errors/errors'); +const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('getInboxCaseMessagesForUserInteractor', () => { + it('throws unauthorized for a user without MESSAGES permission', async () => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitioner, + userId: '9bd0308c-2b06-4589-b36e-242398bea31b', + }); + + await expect( + getInboxCaseMessagesForUserInteractor({ + applicationContext, + }), + ).rejects.toThrow(UnauthorizedError); + }); + + it('retrieves the case messages from persistence and returns them', async () => { + const caseMessageData = { + caseId: '7a130321-0a76-43bc-b3eb-64a18f07987d', + createdAt: '2019-03-01T21:40:46.415Z', + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + entityName: 'CaseMessage', + from: 'Test Petitionsclerk2', + fromSection: 'petitions', + fromUserId: 'fe6eeadd-e4e8-4e56-9ddf-0ebe9516df6b', + message: "How's it going?", + messageId: '9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + pk: 'case|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + sk: 'message|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + subject: 'Hey!', + to: 'Test Petitionsclerk', + toSection: 'petitions', + toUserId: 'b427ca37-0df1-48ac-94bb-47aed073d6f7', + }; + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitionsClerk, + userId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + }); + applicationContext + .getPersistenceGateway() + .getUserInboxMessages.mockReturnValue([caseMessageData]); + + const returnedMessages = await getInboxCaseMessagesForUserInteractor({ + applicationContext, + messageId: caseMessageData.messageId, + }); + + expect( + applicationContext.getPersistenceGateway().getUserInboxMessages, + ).toBeCalled(); + expect(returnedMessages).toEqual([omit(caseMessageData, 'pk', 'sk')]); + }); +}); diff --git a/shared/src/business/useCases/messages/getOutboxCaseMessagesForSectionInteractor.js b/shared/src/business/useCases/messages/getOutboxCaseMessagesForSectionInteractor.js new file mode 100644 index 00000000000..8364e2d6609 --- /dev/null +++ b/shared/src/business/useCases/messages/getOutboxCaseMessagesForSectionInteractor.js @@ -0,0 +1,36 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { CaseMessage } = require('../../entities/CaseMessage'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * getOutboxCaseMessagesForSectionInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.section the section to get the outbox messages + * @returns {object} the messages in the section outbox + */ +exports.getOutboxCaseMessagesForSectionInteractor = async ({ + applicationContext, + section, +}) => { + const authorizedUser = applicationContext.getCurrentUser(); + + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.MESSAGES)) { + throw new UnauthorizedError('Unauthorized'); + } + + const messages = await applicationContext + .getPersistenceGateway() + .getSectionOutboxMessages({ + applicationContext, + section, + }); + + return CaseMessage.validateRawCollection(messages, { + applicationContext, + }); +}; diff --git a/shared/src/business/useCases/messages/getOutboxCaseMessagesForSectionInteractor.test.js b/shared/src/business/useCases/messages/getOutboxCaseMessagesForSectionInteractor.test.js new file mode 100644 index 00000000000..92c4bcf8813 --- /dev/null +++ b/shared/src/business/useCases/messages/getOutboxCaseMessagesForSectionInteractor.test.js @@ -0,0 +1,64 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + getOutboxCaseMessagesForSectionInteractor, +} = require('./getOutboxCaseMessagesForSectionInteractor'); +const { + UnauthorizedError, +} = require('../../../../../shared/src/errors/errors'); +const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('getOutboxCaseMessagesForSectionInteractor', () => { + it('throws unauthorized for a user without MESSAGES permission', async () => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitioner, + userId: '9bd0308c-2b06-4589-b36e-242398bea31b', + }); + + await expect( + getOutboxCaseMessagesForSectionInteractor({ + applicationContext, + }), + ).rejects.toThrow(UnauthorizedError); + }); + + it('retrieves the case messages from persistence and returns them', async () => { + const caseMessageData = { + caseId: '7a130321-0a76-43bc-b3eb-64a18f07987d', + createdAt: '2019-03-01T21:40:46.415Z', + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + entityName: 'CaseMessage', + from: 'Test Petitionsclerk2', + fromSection: 'petitions', + fromUserId: 'fe6eeadd-e4e8-4e56-9ddf-0ebe9516df6b', + message: "How's it going?", + messageId: '9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + pk: 'case|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + sk: 'message|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + subject: 'Hey!', + to: 'Test Petitionsclerk', + toSection: 'petitions', + toUserId: 'b427ca37-0df1-48ac-94bb-47aed073d6f7', + }; + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitionsClerk, + userId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + }); + applicationContext + .getPersistenceGateway() + .getSectionOutboxMessages.mockReturnValue([caseMessageData]); + + const returnedMessages = await getOutboxCaseMessagesForSectionInteractor({ + applicationContext, + section: caseMessageData.section, + }); + + expect( + applicationContext.getPersistenceGateway().getSectionOutboxMessages, + ).toBeCalled(); + expect(returnedMessages).toEqual([omit(caseMessageData, 'pk', 'sk')]); + }); +}); diff --git a/shared/src/business/useCases/messages/getOutboxCaseMessagesForUserInteractor.js b/shared/src/business/useCases/messages/getOutboxCaseMessagesForUserInteractor.js new file mode 100644 index 00000000000..ee24a5e7d9b --- /dev/null +++ b/shared/src/business/useCases/messages/getOutboxCaseMessagesForUserInteractor.js @@ -0,0 +1,36 @@ +const { + isAuthorized, + ROLE_PERMISSIONS, +} = require('../../../authorization/authorizationClientService'); +const { CaseMessage } = require('../../entities/CaseMessage'); +const { UnauthorizedError } = require('../../../errors/errors'); + +/** + * getOutboxCaseMessagesForUserInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.userId the user to get the outbox messages + * @returns {object} the messages in the user outbox + */ +exports.getOutboxCaseMessagesForUserInteractor = async ({ + applicationContext, + userId, +}) => { + const authorizedUser = applicationContext.getCurrentUser(); + + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.MESSAGES)) { + throw new UnauthorizedError('Unauthorized'); + } + + const messages = await applicationContext + .getPersistenceGateway() + .getUserOutboxMessages({ + applicationContext, + userId, + }); + + return CaseMessage.validateRawCollection(messages, { + applicationContext, + }); +}; diff --git a/shared/src/business/useCases/messages/getOutboxCaseMessagesForUserInteractor.test.js b/shared/src/business/useCases/messages/getOutboxCaseMessagesForUserInteractor.test.js new file mode 100644 index 00000000000..a2b20d7ddb8 --- /dev/null +++ b/shared/src/business/useCases/messages/getOutboxCaseMessagesForUserInteractor.test.js @@ -0,0 +1,64 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + getOutboxCaseMessagesForUserInteractor, +} = require('./getOutboxCaseMessagesForUserInteractor'); +const { + UnauthorizedError, +} = require('../../../../../shared/src/errors/errors'); +const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); + +describe('getOutboxCaseMessagesForUserInteractor', () => { + it('throws unauthorized for a user without MESSAGES permission', async () => { + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitioner, + userId: '9bd0308c-2b06-4589-b36e-242398bea31b', + }); + + await expect( + getOutboxCaseMessagesForUserInteractor({ + applicationContext, + }), + ).rejects.toThrow(UnauthorizedError); + }); + + it('retrieves the case messages from persistence and returns them', async () => { + const caseMessageData = { + caseId: '7a130321-0a76-43bc-b3eb-64a18f07987d', + createdAt: '2019-03-01T21:40:46.415Z', + docketNumber: '123-45', + docketNumberWithSuffix: '123-45S', + entityName: 'CaseMessage', + from: 'Test Petitionsclerk2', + fromSection: 'petitions', + fromUserId: 'fe6eeadd-e4e8-4e56-9ddf-0ebe9516df6b', + message: "How's it going?", + messageId: '9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + pk: 'case|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + sk: 'message|9ca37b65-9aac-4621-b5d7-e4a7c8a26a21', + subject: 'Hey!', + to: 'Test Petitionsclerk', + toSection: 'petitions', + toUserId: 'b427ca37-0df1-48ac-94bb-47aed073d6f7', + }; + applicationContext.getCurrentUser.mockReturnValue({ + role: ROLES.petitionsClerk, + userId: 'b9fcabc8-3c83-4cbf-9f4a-d2ecbdc591e1', + }); + applicationContext + .getPersistenceGateway() + .getUserOutboxMessages.mockReturnValue([caseMessageData]); + + const returnedMessages = await getOutboxCaseMessagesForUserInteractor({ + applicationContext, + userId: caseMessageData.userId, + }); + + expect( + applicationContext.getPersistenceGateway().getUserOutboxMessages, + ).toBeCalled(); + expect(returnedMessages).toEqual([omit(caseMessageData, 'pk', 'sk')]); + }); +}); diff --git a/shared/src/business/useCases/messages/validateCreateCaseMessageInteractor.js b/shared/src/business/useCases/messages/validateCreateCaseMessageInteractor.js new file mode 100644 index 00000000000..82b8ec62b02 --- /dev/null +++ b/shared/src/business/useCases/messages/validateCreateCaseMessageInteractor.js @@ -0,0 +1,18 @@ +const { NewCaseMessage } = require('../../entities/NewCaseMessage'); + +/** + * validateCreateCaseMessageInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {object} providers.message the message data + * @returns {object} errors (null if no errors) + */ +exports.validateCreateCaseMessageInteractor = ({ + applicationContext, + message, +}) => { + return new NewCaseMessage(message, { + applicationContext, + }).getFormattedValidationErrors(); +}; diff --git a/shared/src/business/useCases/messages/validateCreateCaseMessageInteractor.test.js b/shared/src/business/useCases/messages/validateCreateCaseMessageInteractor.test.js new file mode 100644 index 00000000000..0d137daf6df --- /dev/null +++ b/shared/src/business/useCases/messages/validateCreateCaseMessageInteractor.test.js @@ -0,0 +1,37 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + validateCreateCaseMessageInteractor, +} = require('./validateCreateCaseMessageInteractor'); + +describe('validateCreateCaseMessageInteractor', () => { + it('returns null when no errors exist in the CaseMessage', () => { + const errors = validateCreateCaseMessageInteractor({ + applicationContext, + message: { + message: 'yup', + subject: 'hi', + toSection: 'petitions', + toUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + }, + }); + + expect(errors).toBeNull(); + }); + + it('returns an error when a subject is missing', () => { + const errors = validateCreateCaseMessageInteractor({ + applicationContext, + message: { + message: 'yup', + toSection: 'petitions', + toUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + }, + }); + + expect(errors).toMatchObject({ + subject: 'Enter a subject line', + }); + }); +}); diff --git a/shared/src/business/useCases/migrateCaseInteractor.js b/shared/src/business/useCases/migrateCaseInteractor.js index 0bdd5e66af7..25cd7a3c146 100644 --- a/shared/src/business/useCases/migrateCaseInteractor.js +++ b/shared/src/business/useCases/migrateCaseInteractor.js @@ -36,8 +36,6 @@ exports.migrateCaseInteractor = async ({ }, ); - caseToAdd.caseCaption = Case.getCaseCaption(caseToAdd); - await applicationContext.getPersistenceGateway().createCase({ applicationContext, caseToCreate: caseToAdd.validate().toRawObject(), diff --git a/shared/src/business/useCases/migrateCaseInteractor.test.js b/shared/src/business/useCases/migrateCaseInteractor.test.js index 8b60ed24dd5..78a5b11901a 100644 --- a/shared/src/business/useCases/migrateCaseInteractor.test.js +++ b/shared/src/business/useCases/migrateCaseInteractor.test.js @@ -1,7 +1,7 @@ const { applicationContext } = require('../test/createTestApplicationContext'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { migrateCaseInteractor } = require('./migrateCaseInteractor'); const { MOCK_CASE } = require('../../test/mockCase.js'); +const { PARTY_TYPES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); const DATE = '2018-11-21T20:49:28.192Z'; @@ -42,6 +42,7 @@ describe('migrateCaseInteractor', () => { }); caseMetadata = { + caseCaption: 'Custom Caption', caseType: 'Other', contactPrimary: { address1: '99 South Oak Lane', @@ -60,7 +61,7 @@ describe('migrateCaseInteractor', () => { documents: MOCK_CASE.documents, filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitionFile: new File([], 'test.pdf'), petitionFileSize: 1, preferredTrialCity: 'Fresno, California', @@ -91,7 +92,7 @@ describe('migrateCaseInteractor', () => { docketNumber: '00101-00', filingType: 'Myself', hasIrsNotice: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', procedureType: 'Small', }, diff --git a/shared/src/business/useCases/opinionAdvancedSearchInteractor.js b/shared/src/business/useCases/opinionAdvancedSearchInteractor.js index 12e982b3381..fc002f73d08 100644 --- a/shared/src/business/useCases/opinionAdvancedSearchInteractor.js +++ b/shared/src/business/useCases/opinionAdvancedSearchInteractor.js @@ -5,7 +5,9 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../authorization/authorizationClientService'); -const { Document } = require('../../business/entities/Document'); +const { + OPINION_DOCUMENT_TYPES, +} = require('../../business/entities/EntityConstants'); const { UnauthorizedError } = require('../../errors/errors'); /** @@ -56,7 +58,7 @@ exports.opinionAdvancedSearchInteractor = async ({ .getPersistenceGateway() .advancedDocumentSearch({ applicationContext, - documentEventCodes: Document.OPINION_DOCUMENT_TYPES, + documentEventCodes: OPINION_DOCUMENT_TYPES, judgeType: 'judge', ...rawSearch, }); diff --git a/shared/src/business/useCases/opinionAdvancedSearchInteractor.test.js b/shared/src/business/useCases/opinionAdvancedSearchInteractor.test.js index 06643ba648d..7528fe65496 100644 --- a/shared/src/business/useCases/opinionAdvancedSearchInteractor.test.js +++ b/shared/src/business/useCases/opinionAdvancedSearchInteractor.test.js @@ -1,8 +1,10 @@ +const { + OPINION_DOCUMENT_TYPES, +} = require('../../business/entities/EntityConstants'); const { opinionAdvancedSearchInteractor, } = require('./opinionAdvancedSearchInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Document } = require('../../business/entities/Document'); describe('opinionAdvancedSearchInteractor', () => { beforeEach(() => { @@ -96,7 +98,7 @@ describe('opinionAdvancedSearchInteractor', () => { applicationContext.getPersistenceGateway().advancedDocumentSearch.mock .calls[0][0], ).toMatchObject({ - documentEventCodes: Document.OPINION_DOCUMENT_TYPES, + documentEventCodes: OPINION_DOCUMENT_TYPES, }); }); }); diff --git a/shared/src/business/useCases/orderAdvancedSearchInteractor.js b/shared/src/business/useCases/orderAdvancedSearchInteractor.js index e5db9ac380d..ecaf06ccc5c 100644 --- a/shared/src/business/useCases/orderAdvancedSearchInteractor.js +++ b/shared/src/business/useCases/orderAdvancedSearchInteractor.js @@ -5,8 +5,10 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../authorization/authorizationClientService'); +const { + ORDER_DOCUMENT_TYPES, +} = require('../../business/entities/EntityConstants'); const { caseSearchFilter } = require('../utilities/caseFilter'); -const { Document } = require('../../business/entities/Document'); const { UnauthorizedError } = require('../../errors/errors'); /** @@ -53,7 +55,7 @@ exports.orderAdvancedSearchInteractor = async ({ .getPersistenceGateway() .advancedDocumentSearch({ applicationContext, - documentEventCodes: Document.ORDER_DOCUMENT_TYPES, + documentEventCodes: ORDER_DOCUMENT_TYPES, judgeType: 'signedJudgeName', ...rawSearch, }); diff --git a/shared/src/business/useCases/orderAdvancedSearchInteractor.test.js b/shared/src/business/useCases/orderAdvancedSearchInteractor.test.js index ec9a5172ff6..a139cc29b3d 100644 --- a/shared/src/business/useCases/orderAdvancedSearchInteractor.test.js +++ b/shared/src/business/useCases/orderAdvancedSearchInteractor.test.js @@ -1,8 +1,10 @@ +const { + ORDER_DOCUMENT_TYPES, +} = require('../../business/entities/EntityConstants'); const { orderAdvancedSearchInteractor, } = require('./orderAdvancedSearchInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Document } = require('../../business/entities/Document'); describe('orderAdvancedSearchInteractor', () => { beforeEach(() => { @@ -92,7 +94,7 @@ describe('orderAdvancedSearchInteractor', () => { applicationContext.getPersistenceGateway().advancedDocumentSearch.mock .calls[0][0], ).toMatchObject({ - documentEventCodes: Document.ORDER_DOCUMENT_TYPES, + documentEventCodes: ORDER_DOCUMENT_TYPES, }); }); }); diff --git a/shared/src/business/useCases/pendingItems/fetchPendingItemsInteractor.test.js b/shared/src/business/useCases/pendingItems/fetchPendingItemsInteractor.test.js index 525f4542dfe..5c1e00d39ce 100644 --- a/shared/src/business/useCases/pendingItems/fetchPendingItemsInteractor.test.js +++ b/shared/src/business/useCases/pendingItems/fetchPendingItemsInteractor.test.js @@ -1,7 +1,7 @@ const { fetchPendingItemsInteractor, } = require('./fetchPendingItemsInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('fetchPendingItemsInteractor', () => { let searchSpy; @@ -10,7 +10,7 @@ describe('fetchPendingItemsInteractor', () => { environment: { stage: 'local' }, getCurrentUser: () => { return { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; }, @@ -42,7 +42,7 @@ describe('fetchPendingItemsInteractor', () => { it('should throw an unauthorized error if the user does not have access to blocked cases', async () => { applicationContext.getCurrentUser = () => { return { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; }; diff --git a/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.js b/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.js index cd00ce48c74..a408d229274 100644 --- a/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.js +++ b/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.js @@ -34,11 +34,13 @@ exports.generatePrintablePendingReportInteractor = async ({ const formattedPendingItems = pendingItems.map(pendingItem => ({ ...pendingItem, - associatedJudgeFormatted: pendingItem.associatedJudge.replace( - /^Judge\s+/, - '', - ), + associatedJudgeFormatted: applicationContext + .getUtilities() + .formatJudgeName(pendingItem.associatedJudge), caseTitle: applicationContext.getCaseTitle(pendingItem.caseCaption || ''), + docketNumberWithSuffix: `${pendingItem.docketNumber}${ + pendingItem.docketNumberSuffix || '' + }`, formattedFiledDate: applicationContext .getUtilities() .formatDateString(pendingItem.receivedAt, 'MMDDYY'), @@ -56,7 +58,9 @@ exports.generatePrintablePendingReportInteractor = async ({ applicationContext, caseId, }); - reportTitle = `Docket ${caseResult.docketNumber}`; + reportTitle = `Docket ${caseResult.docketNumber}${ + caseResult.docketNumberSuffix || '' + }`; } const pdf = await applicationContext.getDocumentGenerators().pendingReport({ diff --git a/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.test.js b/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.test.js index 7bde6468a79..480fb80de41 100644 --- a/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.test.js +++ b/shared/src/business/useCases/pendingItems/generatePrintablePendingReportInteractor.test.js @@ -5,14 +5,10 @@ const { generatePrintablePendingReportInteractor, } = require('./generatePrintablePendingReportInteractor'); const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('generatePrintablePendingReportInteractor', () => { beforeAll(() => { - applicationContext - .getPersistenceGateway() - .getCaseByCaseId.mockReturnValue(MOCK_CASE); - applicationContext.getStorageClient.mockReturnValue({ upload: jest.fn((params, callback) => callback()), }); @@ -21,12 +17,15 @@ describe('generatePrintablePendingReportInteractor', () => { { associatedJudge: 'Judge Armen', caseCaption: 'Test Caption, Petitioner', + docketNumber: '123-45', documentTitle: 'Test Document Title', receivedAt: '2020-01-01T12:00:00.000Z', }, { associatedJudge: 'Judge Buch', caseCaption: 'Test Caption Two, Petitioner(s)', + docketNumber: '234-56', + docketNumberSuffix: 'S', documentType: 'Test Document Type', receivedAt: '2020-02-02T12:00:00.000Z', }, @@ -39,9 +38,13 @@ describe('generatePrintablePendingReportInteractor', () => { beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); + + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(MOCK_CASE); }); afterEach(() => { @@ -50,7 +53,7 @@ describe('generatePrintablePendingReportInteractor', () => { it('should throw an unauthorized error if the user does not have access', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); @@ -93,6 +96,7 @@ describe('generatePrintablePendingReportInteractor', () => { associatedJudge: 'Judge Armen', associatedJudgeFormatted: 'Armen', caseTitle: 'Test Caption', + docketNumberWithSuffix: '123-45', formattedFiledDate: '01/01/20', formattedName: 'Test Document Title', }, @@ -100,6 +104,7 @@ describe('generatePrintablePendingReportInteractor', () => { associatedJudge: 'Judge Buch', associatedJudgeFormatted: 'Buch', caseTitle: 'Test Caption Two', + docketNumberWithSuffix: '234-56S', formattedFiledDate: '02/02/20', formattedName: 'Test Document Type', }, @@ -142,6 +147,21 @@ describe('generatePrintablePendingReportInteractor', () => { expect(subtitle).toEqual(`Docket ${MOCK_CASE.docketNumber}`); }); + it('should generate a subtitle with the docket number suffix if present', async () => { + MOCK_CASE.docketNumberSuffix = 'W'; + + await generatePrintablePendingReportInteractor({ + applicationContext, + caseId: '123', + }); + + const { + subtitle, + } = applicationContext.getDocumentGenerators().pendingReport.mock.calls[0][0].data; + + expect(subtitle).toEqual(`Docket ${MOCK_CASE.docketNumber}W`); + }); + it('calls the document generator function', async () => { await generatePrintablePendingReportInteractor({ applicationContext, diff --git a/shared/src/business/useCases/practitioners/createPractitionerUserInteractor.test.js b/shared/src/business/useCases/practitioners/createPractitionerUserInteractor.test.js index f77f09a0607..f835a0e6262 100644 --- a/shared/src/business/useCases/practitioners/createPractitionerUserInteractor.test.js +++ b/shared/src/business/useCases/practitioners/createPractitionerUserInteractor.test.js @@ -4,8 +4,8 @@ const { const { createPractitionerUserInteractor, } = require('./createPractitionerUserInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const mockUser = { admissionsDate: '2019-03-01T21:40:46.415Z', @@ -19,8 +19,8 @@ const mockUser = { name: 'Test Attorney', originalBarState: 'Oklahoma', practitionerType: 'Attorney', - role: User.ROLES.privatePractitioner, - userId: 'practitioner1@example.com', + role: ROLES.privatePractitioner, + userId: '07044afe-641b-4d75-a84f-0698870b7650', }; describe('create practitioner user', () => { @@ -49,8 +49,8 @@ describe('create practitioner user', () => { it('throws unauthorized for a non-internal user', async () => { testUser = { - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '6a2a8f95-0223-442e-8e55-5f094c6bca15', }; await expect( diff --git a/shared/src/business/useCases/practitioners/getPractitionerByBarNumberInteractor.test.js b/shared/src/business/useCases/practitioners/getPractitionerByBarNumberInteractor.test.js index 4e9a8f6c153..a98c6e69c10 100644 --- a/shared/src/business/useCases/practitioners/getPractitionerByBarNumberInteractor.test.js +++ b/shared/src/business/useCases/practitioners/getPractitionerByBarNumberInteractor.test.js @@ -4,6 +4,7 @@ const { const { getPractitionerByBarNumberInteractor, } = require('./getPractitionerByBarNumberInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); describe('getPractitionerByBarNumberInteractor', () => { @@ -11,7 +12,7 @@ describe('getPractitionerByBarNumberInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '1005d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -28,7 +29,7 @@ describe('getPractitionerByBarNumberInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -46,7 +47,7 @@ describe('getPractitionerByBarNumberInteractor', () => { name: 'Private Practitioner', originalBarState: 'Oklahoma', practitionerType: 'Attorney', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -73,7 +74,7 @@ describe('getPractitionerByBarNumberInteractor', () => { name: 'Private Practitioner', originalBarState: 'Oklahoma', practitionerType: 'Attorney', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', suffix: undefined, token: undefined, @@ -85,7 +86,7 @@ describe('getPractitionerByBarNumberInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -103,7 +104,7 @@ describe('getPractitionerByBarNumberInteractor', () => { name: 'IRS Practitioner', originalBarState: 'Oklahoma', practitionerType: 'Attorney', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, section: 'privatePractitioner', userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -142,7 +143,7 @@ describe('getPractitionerByBarNumberInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); diff --git a/shared/src/business/useCases/practitioners/getPractitionersByNameInteractor.test.js b/shared/src/business/useCases/practitioners/getPractitionersByNameInteractor.test.js index a833a9cbe6c..b0c02f24495 100644 --- a/shared/src/business/useCases/practitioners/getPractitionersByNameInteractor.test.js +++ b/shared/src/business/useCases/practitioners/getPractitionersByNameInteractor.test.js @@ -4,19 +4,19 @@ const { const { getPractitionersByNameInteractor, } = require('./getPractitionersByNameInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getPractitionersByNameInteractor', () => { beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsClerk', }); }); it('returns an unauthorized error on petitioner user role', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); diff --git a/shared/src/business/useCases/practitioners/updatePractitionerUserInteractor.test.js b/shared/src/business/useCases/practitioners/updatePractitionerUserInteractor.test.js index 8c2019e2855..2d4bd67e9b8 100644 --- a/shared/src/business/useCases/practitioners/updatePractitionerUserInteractor.test.js +++ b/shared/src/business/useCases/practitioners/updatePractitionerUserInteractor.test.js @@ -4,8 +4,8 @@ const { const { updatePractitionerUserInteractor, } = require('./updatePractitionerUserInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const mockUser = { admissionsDate: '2019-03-01T21:40:46.415Z', @@ -20,8 +20,8 @@ const mockUser = { name: 'Test Attorney', originalBarState: 'Oklahoma', practitionerType: 'Attorney', - role: User.ROLES.privatePractitioner, - userId: 'practitioner1@example.com', + role: ROLES.privatePractitioner, + userId: 'df56e4f8-b302-46ec-b9b3-a6a5e2142092', }; describe('update practitioner user', () => { @@ -67,7 +67,7 @@ describe('update practitioner user', () => { .getPersistenceGateway() .getPractitionerByBarNumber.mockResolvedValue({ ...mockUser, - userId: '2', + userId: '2c14ebbc-a6e1-4267-b6b7-e329e592ec93', }); await expect( @@ -78,7 +78,7 @@ describe('update practitioner user', () => { ...mockUser, barNumber: 'AB1111', email: 'bc@example.com', - userId: '1', + userId: '9ea9732c-9751-4159-9619-bd27556eb9bc', }, }), ).rejects.toThrow('Bar number does not match user data.'); @@ -86,7 +86,7 @@ describe('update practitioner user', () => { it('throws unauthorized for a non-internal user', async () => { testUser = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; diff --git a/shared/src/business/useCases/prioritizeCaseInteractor.test.js b/shared/src/business/useCases/prioritizeCaseInteractor.test.js index f98d8d34ca4..6991d356d41 100644 --- a/shared/src/business/useCases/prioritizeCaseInteractor.test.js +++ b/shared/src/business/useCases/prioritizeCaseInteractor.test.js @@ -1,13 +1,13 @@ const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { MOCK_CASE } = require('../../test/mockCase'); const { prioritizeCaseInteractor } = require('./prioritizeCaseInteractor'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('prioritizeCaseInteractor', () => { it('should update the case with the highPriority flag set as true and attach a reason', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); applicationContext @@ -47,13 +47,13 @@ describe('prioritizeCaseInteractor', () => { it('should throw an error if the case status is calendared', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue( Promise.resolve({ ...MOCK_CASE, - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, }), ); diff --git a/shared/src/business/useCases/processStreamRecordsInteractor.js b/shared/src/business/useCases/processStreamRecordsInteractor.js index 869e23dbfc5..679bde89a25 100644 --- a/shared/src/business/useCases/processStreamRecordsInteractor.js +++ b/shared/src/business/useCases/processStreamRecordsInteractor.js @@ -99,7 +99,7 @@ const filterRecords = async ({ applicationContext, records }) => { applicationContext, documentId: document.documentContentsId, protocol: 'S3', - useTempBucket: true, + useTempBucket: false, }); const { documentContents } = JSON.parse(buffer.toString()); diff --git a/shared/src/business/useCases/processStreamRecordsInteractor.test.js b/shared/src/business/useCases/processStreamRecordsInteractor.test.js index 6d7e6e59075..f54c99df154 100644 --- a/shared/src/business/useCases/processStreamRecordsInteractor.test.js +++ b/shared/src/business/useCases/processStreamRecordsInteractor.test.js @@ -230,17 +230,20 @@ describe('processStreamRecordsInteractor', () => { it('attempts to reindex if bulk indexing returns error data', async () => { applicationContext.getSearchClient().bulk.mockResolvedValue({ - body: { - errors: [{ badError: true }], - items: [ - { - index: { error: false }, - }, - { - index: { error: true }, + errors: true, + items: [ + { + index: {}, + }, + { + index: { + error: { + reason: 'document missing', + type: 'document_missing_exception', + }, }, - ], - }, + }, + ], }); await processStreamRecordsInteractor({ @@ -276,24 +279,25 @@ describe('processStreamRecordsInteractor', () => { expect(applicationContext.getSearchClient().index).toBeCalled(); expect( applicationContext.getSearchClient().index.mock.calls[0][0], - ).toMatchObject({ - body: { caseId: { S: '2' } }, - }); + ).toMatchObject({ body: { caseId: { S: '2' } } }); }); it('creates a reindex record if bulk indexing returns error data and individual indexing fails', async () => { applicationContext.getSearchClient().bulk.mockResolvedValue({ - body: { - errors: [{ badError: true }], - items: [ - { - index: { error: false }, - }, - { - index: { error: true }, + errors: true, + items: [ + { + index: { error: false }, + }, + { + index: { + error: { + reason: 'document missing', + type: 'document_missing_exception', + }, }, - ], - }, + }, + ], }); applicationContext.getSearchClient().index.mockImplementation(() => { throw new Error('bad!'); diff --git a/shared/src/business/useCases/public/casePublicSearchInteractor.test.js b/shared/src/business/useCases/public/casePublicSearchInteractor.test.js index 7114e83e977..dcca57f4625 100644 --- a/shared/src/business/useCases/public/casePublicSearchInteractor.test.js +++ b/shared/src/business/useCases/public/casePublicSearchInteractor.test.js @@ -60,6 +60,7 @@ describe('casePublicSearchInteractor', () => { createdAt: undefined, docketNumber: '123-19', docketNumberSuffix: 'S', + docketNumberWithSuffix: '123-19S', docketRecord: [], documents: [], isSealed: false, @@ -73,6 +74,7 @@ describe('casePublicSearchInteractor', () => { createdAt: undefined, docketNumber: '456-19', docketNumberSuffix: 'S', + docketNumberWithSuffix: '456-19S', docketRecord: [], documents: [], isSealed: false, @@ -111,6 +113,7 @@ describe('casePublicSearchInteractor', () => { createdAt: undefined, docketNumber: '123-19', docketNumberSuffix: 'S', + docketNumberWithSuffix: '123-19S', docketRecord: [], documents: [], isSealed: false, @@ -149,6 +152,7 @@ describe('casePublicSearchInteractor', () => { createdAt: undefined, docketNumber: '123-19', docketNumberSuffix: 'S', + docketNumberWithSuffix: '123-19S', docketRecord: [], documents: [], isSealed: false, diff --git a/shared/src/business/useCases/public/getCaseForPublicDocketSearchInteractor.js b/shared/src/business/useCases/public/getCaseForPublicDocketSearchInteractor.js new file mode 100644 index 00000000000..5145b92742c --- /dev/null +++ b/shared/src/business/useCases/public/getCaseForPublicDocketSearchInteractor.js @@ -0,0 +1,55 @@ +const { Case } = require('../../entities/cases/Case'); +const { NotFoundError, UnauthorizedError } = require('../../../errors/errors'); +const { PublicCase } = require('../../entities/cases/PublicCase'); + +/** + * getCaseForPublicDocketSearchInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to get + * @returns {object} the case data + */ +exports.getCaseForPublicDocketSearchInteractor = async ({ + applicationContext, + docketNumber, +}) => { + let caseRecord; + + if (Case.isValidCaseId(docketNumber)) { + caseRecord = await applicationContext + .getPersistenceGateway() + .getCaseByCaseId({ + applicationContext, + docketNumber, + }); + } else { + caseRecord = await applicationContext + .getPersistenceGateway() + .getCaseByDocketNumber({ + applicationContext, + docketNumber: Case.stripLeadingZeros(docketNumber), + }); + } + + if (!caseRecord) { + const error = new NotFoundError(`Case ${docketNumber} was not found.`); + error.skipLogging = true; + throw error; + } + + let caseDetailRaw; + + if (caseRecord.sealedDate || caseRecord.isSealed) { + const error = new UnauthorizedError(`Case ${docketNumber} is sealed.`); + error.skipLogging = true; + throw error; + } else { + caseDetailRaw = new PublicCase(caseRecord, { + applicationContext, + }) + .validate() + .toRawObject(); + } + return caseDetailRaw; +}; diff --git a/shared/src/business/useCases/public/getCaseForPublicDocketSearchInteractor.test.js b/shared/src/business/useCases/public/getCaseForPublicDocketSearchInteractor.test.js new file mode 100644 index 00000000000..88a0d298274 --- /dev/null +++ b/shared/src/business/useCases/public/getCaseForPublicDocketSearchInteractor.test.js @@ -0,0 +1,61 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + getCaseForPublicDocketSearchInteractor, +} = require('./getCaseForPublicDocketSearchInteractor'); +const { MOCK_CASE } = require('../../../test/mockCase'); + +describe('Get case for public docket record search', () => { + beforeEach(() => { + applicationContext + .getPersistenceGateway() + .getCaseByDocketNumber.mockResolvedValue(Promise.resolve(MOCK_CASE)); + }); + + it('should throw an unauthorized error when the found case is sealed', async () => { + applicationContext + .getPersistenceGateway() + .getCaseByDocketNumber.mockResolvedValue({ + ...MOCK_CASE, + isSealed: true, + sealedDate: '2020/05/05', + }); + await expect( + getCaseForPublicDocketSearchInteractor({ + applicationContext, + docketNumber: MOCK_CASE.docketNumber, + }), + ).rejects.toThrow('Case 101-18 is sealed.'); + }); + + it('searches for a case by docket number', async () => { + applicationContext + .getPersistenceGateway() + .getCaseByDocketNumber.mockResolvedValue(MOCK_CASE); + + const caseRecord = await getCaseForPublicDocketSearchInteractor({ + applicationContext, + docketNumber: MOCK_CASE.docketNumber, + }); + + expect(caseRecord.caseId).toEqual('c54ba5a9-b37b-479d-9201-067ec6e335bb'); + expect( + applicationContext.getPersistenceGateway().getCaseByDocketNumber.mock + .calls[0][0].docketNumber, + ).toEqual(MOCK_CASE.docketNumber); + }); + + it('should throw an error when unable to find a case by docket number', async () => { + applicationContext + .getPersistenceGateway() + .getCaseByDocketNumber.mockResolvedValue(undefined); + + await expect( + getCaseForPublicDocketSearchInteractor({ + applicationContext, + docketNumber: '00-111', + }), + ).rejects.toThrow('Case 00-111 was not found.'); + }); +}); diff --git a/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.js b/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.js index 6b2cc951490..29917d1abce 100644 --- a/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.js +++ b/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.js @@ -1,5 +1,5 @@ const { PublicUser } = require('../../entities/PublicUser'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); /** * getJudgesForPublicSearchInteractor @@ -13,7 +13,7 @@ exports.getJudgesForPublicSearchInteractor = async ({ applicationContext }) => { .getPersistenceGateway() .getUsersInSection({ applicationContext, - section: User.ROLES.judge, + section: ROLES.judge, }); return PublicUser.validateRawCollection(rawJudges, { applicationContext }); diff --git a/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.test.js b/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.test.js index b960bcf8256..3669bf25842 100644 --- a/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.test.js +++ b/shared/src/business/useCases/public/getJudgesForPublicSearchInteractor.test.js @@ -4,7 +4,7 @@ const { const { getJudgesForPublicSearchInteractor, } = require('./getJudgesForPublicSearchInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getJudgesForPublicSearchInteractor', () => { it('strips out all non public data', async () => { @@ -37,7 +37,7 @@ describe('getJudgesForPublicSearchInteractor', () => { expect( applicationContext.getPersistenceGateway().getUsersInSection, - ).toHaveBeenCalledWith({ applicationContext, section: User.ROLES.judge }); + ).toHaveBeenCalledWith({ applicationContext, section: ROLES.judge }); expect(results).toEqual([ { judgeFullName: 'Lila A. Fenwick', diff --git a/shared/src/business/useCases/public/getPublicCaseInteractor.js b/shared/src/business/useCases/public/getPublicCaseInteractor.js index 7b8fd32c25f..185ffb77b1f 100644 --- a/shared/src/business/useCases/public/getPublicCaseInteractor.js +++ b/shared/src/business/useCases/public/getPublicCaseInteractor.js @@ -21,7 +21,7 @@ exports.getPublicCaseInteractor = async ({ applicationContext, caseId }) => { applicationContext, caseId, }); - } else if (Case.isValidDocketNumber(caseId)) { + } else { caseRecord = await applicationContext .getPersistenceGateway() .getCaseByDocketNumber({ diff --git a/shared/src/business/useCases/public/getTodaysOpinionsInteractor.js b/shared/src/business/useCases/public/getTodaysOpinionsInteractor.js new file mode 100644 index 00000000000..59eee37bde7 --- /dev/null +++ b/shared/src/business/useCases/public/getTodaysOpinionsInteractor.js @@ -0,0 +1,30 @@ +const { + createEndOfDayISO, + createISODateString, + createStartOfDayISO, + deconstructDate, +} = require('../../utilities/DateHandler'); +const { OPINION_DOCUMENT_TYPES } = require('../../entities/EntityConstants'); + +/** + * getTodaysOpinionsInteractor + * + * @param {object} providers the providers object containing applicationContext + * @param {object} providers.applicationContext application context object + * @returns {array} an array of opinions (if any) + */ +exports.getTodaysOpinionsInteractor = async ({ applicationContext }) => { + const { day, month, year } = deconstructDate(createISODateString()); + const currentDateStart = createStartOfDayISO({ day, month, year }); + const currentDateEnd = createEndOfDayISO({ day, month, year }); + + return await applicationContext + .getPersistenceGateway() + .advancedDocumentSearch({ + applicationContext, + documentEventCodes: OPINION_DOCUMENT_TYPES, + endDate: currentDateEnd, + judgeType: 'judge', + startDate: currentDateStart, + }); +}; diff --git a/shared/src/business/useCases/public/getTodaysOpinionsInteractor.test.js b/shared/src/business/useCases/public/getTodaysOpinionsInteractor.test.js new file mode 100644 index 00000000000..0daa162ee6f --- /dev/null +++ b/shared/src/business/useCases/public/getTodaysOpinionsInteractor.test.js @@ -0,0 +1,71 @@ +const { + applicationContext, +} = require('../../test/createTestApplicationContext'); +const { + createEndOfDayISO, + createISODateString, + createStartOfDayISO, + deconstructDate, +} = require('../../utilities/DateHandler'); +const { + getTodaysOpinionsInteractor, +} = require('./getTodaysOpinionsInteractor'); +const { OPINION_DOCUMENT_TYPES } = require('../../entities/EntityConstants'); + +describe('getTodaysOpinionsInteractor', () => { + const mockOpinionSearchResult = [ + { + caseCaption: 'Reuben Blair, Petitioner', + caseId: '24fcb050-9c95-4d69-a149-96acba0196b8', + contactPrimary: { + address1: '66 East Clarendon Parkway', + address2: 'Ut culpa cum sint ', + address3: 'In laboris hic volup', + city: 'Omnis dignissimos at', + countryType: 'domestic', + email: 'petitioner', + name: 'Reuben Blair', + phone: '+1 (338) 996-7072', + postalCode: '92017', + serviceIndicator: 'Electronic', + state: 'DC', + }, + contactSecondary: {}, + docketNumber: '103-20', + docketNumberSuffix: 'L', + documentId: '6945cdff-fd12-422b-bf2c-63b792b7f618', + documentTitle: 'Memorandum Opinion Judge Armen', + filingDate: '2020-05-12T18:42:10.471Z', + irsPractitioners: [], + isSealed: false, + numberOfPages: 1, + privatePractitioners: [], + signedJudgeName: 'Maurice B. Foley', + }, + ]; + + beforeEach(() => { + applicationContext + .getPersistenceGateway() + .advancedDocumentSearch.mockResolvedValue(mockOpinionSearchResult); + }); + + it('should only search for opinion document types', async () => { + await getTodaysOpinionsInteractor({ + applicationContext, + }); + + const { day, month, year } = deconstructDate(createISODateString()); + const currentDateStart = createStartOfDayISO({ day, month, year }); + const currentDateEnd = createEndOfDayISO({ day, month, year }); + + expect( + applicationContext.getPersistenceGateway().advancedDocumentSearch.mock + .calls[0][0], + ).toMatchObject({ + documentEventCodes: OPINION_DOCUMENT_TYPES, + endDate: currentDateEnd, + startDate: currentDateStart, + }); + }); +}); diff --git a/shared/src/business/useCases/public/opinionPublicSearchInteractor.js b/shared/src/business/useCases/public/opinionPublicSearchInteractor.js index 9d1f4ea339a..81c85349d3d 100644 --- a/shared/src/business/useCases/public/opinionPublicSearchInteractor.js +++ b/shared/src/business/useCases/public/opinionPublicSearchInteractor.js @@ -1,5 +1,5 @@ -const { Document } = require('../../entities/Document'); const { DocumentSearch } = require('../../entities/documents/DocumentSearch'); +const { OPINION_DOCUMENT_TYPES } = require('../../entities/EntityConstants'); /** * opinionPublicSearchInteractor @@ -42,7 +42,7 @@ exports.opinionPublicSearchInteractor = async ({ .getPersistenceGateway() .advancedDocumentSearch({ applicationContext, - documentEventCodes: Document.OPINION_DOCUMENT_TYPES, + documentEventCodes: OPINION_DOCUMENT_TYPES, judgeType: 'judge', ...rawSearch, }); diff --git a/shared/src/business/useCases/public/opinionPublicSearchInteractor.test.js b/shared/src/business/useCases/public/opinionPublicSearchInteractor.test.js index f806d26570c..e463b4e03ba 100644 --- a/shared/src/business/useCases/public/opinionPublicSearchInteractor.test.js +++ b/shared/src/business/useCases/public/opinionPublicSearchInteractor.test.js @@ -4,7 +4,7 @@ const { const { opinionPublicSearchInteractor, } = require('./opinionPublicSearchInteractor'); -const { Document } = require('../../entities/Document'); +const { OPINION_DOCUMENT_TYPES } = require('../../entities/EntityConstants'); describe('opinionPublicSearchInteractor', () => { const mockOpinionSearchResult = [ @@ -54,7 +54,7 @@ describe('opinionPublicSearchInteractor', () => { applicationContext.getPersistenceGateway().advancedDocumentSearch.mock .calls[0][0], ).toMatchObject({ - documentEventCodes: Document.OPINION_DOCUMENT_TYPES, + documentEventCodes: OPINION_DOCUMENT_TYPES, }); }); diff --git a/shared/src/business/useCases/public/orderPublicSearchInteractor.js b/shared/src/business/useCases/public/orderPublicSearchInteractor.js index be3628ea520..c86ed161c8b 100644 --- a/shared/src/business/useCases/public/orderPublicSearchInteractor.js +++ b/shared/src/business/useCases/public/orderPublicSearchInteractor.js @@ -1,5 +1,5 @@ -const { Document } = require('../../entities/Document'); const { DocumentSearch } = require('../../entities/documents/DocumentSearch'); +const { ORDER_DOCUMENT_TYPES } = require('../../entities/EntityConstants'); /** * orderPublicSearchInteractor @@ -40,7 +40,7 @@ exports.orderPublicSearchInteractor = async ({ .getPersistenceGateway() .advancedDocumentSearch({ applicationContext, - documentEventCodes: Document.ORDER_DOCUMENT_TYPES, + documentEventCodes: ORDER_DOCUMENT_TYPES, judgeType: 'signedJudgeName', ...rawSearch, }); diff --git a/shared/src/business/useCases/public/orderPublicSearchInteractor.test.js b/shared/src/business/useCases/public/orderPublicSearchInteractor.test.js index ff1ca574e95..9857c70da34 100644 --- a/shared/src/business/useCases/public/orderPublicSearchInteractor.test.js +++ b/shared/src/business/useCases/public/orderPublicSearchInteractor.test.js @@ -4,7 +4,7 @@ const { const { orderPublicSearchInteractor, } = require('./orderPublicSearchInteractor'); -const { Document } = require('../../entities/Document'); +const { ORDER_DOCUMENT_TYPES } = require('../../entities/EntityConstants'); describe('orderPublicSearchInteractor', () => { beforeEach(() => { @@ -56,7 +56,7 @@ describe('orderPublicSearchInteractor', () => { applicationContext.getPersistenceGateway().advancedDocumentSearch.mock .calls[0][0], ).toMatchObject({ - documentEventCodes: Document.ORDER_DOCUMENT_TYPES, + documentEventCodes: ORDER_DOCUMENT_TYPES, }); }); diff --git a/shared/src/business/useCases/removeCasePendingItemInteractor.test.js b/shared/src/business/useCases/removeCasePendingItemInteractor.test.js index 7ea237d2adf..71d13c33287 100644 --- a/shared/src/business/useCases/removeCasePendingItemInteractor.test.js +++ b/shared/src/business/useCases/removeCasePendingItemInteractor.test.js @@ -2,8 +2,9 @@ const { removeCasePendingItemInteractor, } = require('./removeCasePendingItemInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); +const { AUTOMATIC_BLOCKED_REASONS } = require('../entities/EntityConstants'); const { MOCK_CASE } = require('../../test/mockCase'); +const { ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('removeCasePendingItemInteractor', () => { @@ -12,7 +13,7 @@ describe('removeCasePendingItemInteractor', () => { beforeEach(() => { user = new User({ name: 'Petitions Clerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -28,7 +29,7 @@ describe('removeCasePendingItemInteractor', () => { it('should throw an unauthorized error if user is unauthorized for updating a case', async () => { user = new User({ name: 'Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '2c464719-646c-463e-9826-16443500ed88', }); @@ -92,7 +93,7 @@ describe('removeCasePendingItemInteractor', () => { ).toMatchObject({ automaticBlocked: true, automaticBlockedDate: expect.anything(), - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.dueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.dueDate, }); expect( applicationContext.getPersistenceGateway() diff --git a/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.js b/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.js index 8c585cf53cb..280d014fd13 100644 --- a/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.js +++ b/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.js @@ -9,6 +9,7 @@ const { const { Case } = require('../entities/cases/Case'); const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { isEmpty } = require('lodash'); +const { WorkItem } = require('../entities/WorkItem'); /** * saveCaseDetailInternalEditInteractor @@ -24,12 +25,16 @@ exports.saveCaseDetailInternalEditInteractor = async ({ caseId, caseToUpdate, }) => { - const user = applicationContext.getCurrentUser(); + const authorizedUser = applicationContext.getCurrentUser(); - if (!isAuthorized(user, ROLE_PERMISSIONS.UPDATE_CASE)) { + if (!isAuthorized(authorizedUser, ROLE_PERMISSIONS.UPDATE_CASE)) { throw new UnauthorizedError('Unauthorized for update case'); } + const user = await applicationContext + .getPersistenceGateway() + .getUserById({ applicationContext, userId: authorizedUser.userId }); + if (!caseToUpdate || caseId !== caseToUpdate.caseId) { throw new UnprocessableEntityError(); } @@ -90,17 +95,45 @@ exports.saveCaseDetailInternalEditInteractor = async ({ }).secondary.toRawObject(); } - const updatedCase = new Case(fullCase, { applicationContext }) - .setRequestForTrialDocketRecord(fullCase.preferredTrialCity, { - applicationContext, - }) - .validate() - .toRawObject(); - - await applicationContext.getPersistenceGateway().updateCase({ + const caseEntity = new Case(fullCase, { applicationContext }).validate(); + caseEntity.setRequestForTrialDocketRecord(fullCase.preferredTrialCity, { applicationContext, - caseToUpdate: updatedCase, }); - return updatedCase; + if (!caseEntity.isPaper) { + const petitionDocument = caseEntity.getPetitionDocument(); + + const initializeCaseWorkItem = petitionDocument.workItems.find( + workItem => workItem.isInitializeCase, + ); + + await applicationContext.getPersistenceGateway().deleteWorkItemFromInbox({ + applicationContext, + workItem: initializeCaseWorkItem.validate().toRawObject(), + }); + + const workItemEntity = new WorkItem( + { + ...initializeCaseWorkItem, + assigneeId: user.userId, + assigneeName: user.name, + caseIsInProgress: true, + }, + { applicationContext }, + ); + + await applicationContext.getPersistenceGateway().saveWorkItemForPaper({ + applicationContext, + workItem: workItemEntity.validate().toRawObject(), + }); + } + + const updatedCase = await applicationContext + .getPersistenceGateway() + .updateCase({ + applicationContext, + caseToUpdate: caseEntity.validate().toRawObject(), + }); + + return new Case(updatedCase, { applicationContext }).toRawObject(); }; diff --git a/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.test.js b/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.test.js index cb6b2d402ed..0837aaa6c13 100644 --- a/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.test.js +++ b/shared/src/business/useCases/saveCaseDetailInternalEditInteractor.test.js @@ -1,70 +1,87 @@ +const { + CASE_STATUS_TYPES, + PARTY_TYPES, +} = require('../entities/EntityConstants'); const { saveCaseDetailInternalEditInteractor, } = require('./saveCaseDetailInternalEditInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); -const { MOCK_DOCUMENTS } = require('../../test/mockDocuments'); const { omit } = require('lodash'); -const { User } = require('../entities/User'); - -const MOCK_CASE = { - caseCaption: 'Caption', - caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - caseType: 'Other', - contactPrimary: { - address1: '123 Main St', - city: 'Somewhere', - countryType: 'domestic', - email: 'fieri@example.com', - name: 'Guy Fieri', - phone: '1234567890', - postalCode: '12345', - state: 'CA', - }, - createdAt: new Date().toISOString(), - docketNumber: '56789-18', - documents: [ - { - documentId: 'a6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', - }, - { - documentId: 'b6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', - }, - { - documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', - documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', +const { ROLES } = require('../entities/EntityConstants'); + +describe('updateCase', () => { + const MOCK_CASE = { + caseCaption: 'Caption', + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + caseType: 'Other', + contactPrimary: { + address1: '123 Main St', + city: 'Somewhere', + countryType: 'domestic', + email: 'fieri@example.com', + name: 'Guy Fieri', + phone: '1234567890', + postalCode: '12345', + state: 'CA', }, - ], - filingType: 'Myself', - partyType: ContactFactory.PARTY_TYPES.petitioner, - petitioners: [{ name: 'Test Petitioner' }], - preferredTrialCity: 'Washington, District of Columbia', - procedureType: 'Regular', - status: Case.STATUS_TYPES.new, - userId: 'userId', -}; - -beforeAll(() => { - applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + createdAt: new Date().toISOString(), + docketNumber: '56789-18', + documents: [ + { + documentId: 'a6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Petition', + userId: '50c62fa0-dd90-4244-b7c7-9cb2302d7688', + workItems: [ + { + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + docketNumber: '56789-18', + document: { documentId: 'a6b81f4d-1e47-423a-8caf-6d2fdc3d3859' }, + isInitializeCase: true, + isQC: true, + section: 'petitions', + sentBy: 'petitioner', + workItemId: '4a57f4fe-991f-4d4b-bca4-be2a3f5bb5f8', + }, + ], + }, + { + documentId: 'b6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Answer', + userId: '50c62fa0-dd90-4244-b7c7-9cb2302d7688', + }, + { + documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Motion', + userId: '50c62fa0-dd90-4244-b7c7-9cb2302d7688', + }, + ], + filingType: 'Myself', + partyType: PARTY_TYPES.petitioner, + petitioners: [{ name: 'Test Petitioner' }], + preferredTrialCity: 'Washington, District of Columbia', + procedureType: 'Regular', + status: CASE_STATUS_TYPES.new, + userId: 'userId', + }; + + const petitionsClerkUser = { + name: 'petitions clerk', + role: ROLES.petitionsClerk, + userId: '54cddcd9-d012-4874-b74f-73732c95d42b', + }; + + beforeAll(() => { + applicationContext.getCurrentUser.mockReturnValue(petitionsClerkUser); + + applicationContext + .getPersistenceGateway() + .getUserById.mockReturnValue(petitionsClerkUser); + + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(MOCK_CASE); }); - applicationContext - .getPersistenceGateway() - .getCaseByCaseId.mockReturnValue(MOCK_CASE); -}); - -describe('updateCase', () => { it('should throw an error if the caseToUpdate passed in is an invalid case', async () => { await expect( saveCaseDetailInternalEditInteractor({ @@ -84,27 +101,8 @@ describe('updateCase', () => { ).rejects.toThrow('cannot process'); }); - it('should update a case', async () => { - const caseToUpdate = Object.assign(MOCK_CASE); - caseToUpdate.documents = MOCK_DOCUMENTS; - - const updatedCase = await saveCaseDetailInternalEditInteractor({ - applicationContext, - caseId: caseToUpdate.caseId, - caseToUpdate: caseToUpdate, - }); - - const returnedDocument = omit(updatedCase.documents[0], [ - 'createdAt', - 'receivedAt', - ]); - const documentToMatch = omit(MOCK_DOCUMENTS[0], 'createdAt'); - expect(returnedDocument).toMatchObject(documentToMatch); - }); - it('should update the validated documents on a case', async () => { const caseToUpdate = Object.assign(MOCK_CASE); - caseToUpdate.documents = MOCK_DOCUMENTS; const updatedCase = await saveCaseDetailInternalEditInteractor({ applicationContext, @@ -142,7 +140,7 @@ describe('updateCase', () => { filingType: 'Myself and my spouse', hasVerifiedIrsNotice: false, isPaper: false, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, preferredTrialCity: 'Mobile, Alabama', privatePractitioners: [], procedureType: 'Small', @@ -150,13 +148,56 @@ describe('updateCase', () => { }); const returnedDocument = omit(updatedCase.documents[0], 'createdAt'); - const documentToMatch = omit(MOCK_DOCUMENTS[0], 'createdAt'); + const documentToMatch = omit(MOCK_CASE.documents[0], 'createdAt'); expect(returnedDocument).toMatchObject(documentToMatch); }); + it("should move the initialize case work item into the current user's in-progress box if the case is not paper", async () => { + const caseToUpdate = Object.assign(MOCK_CASE); + + await saveCaseDetailInternalEditInteractor({ + applicationContext, + caseId: caseToUpdate.caseId, + caseToUpdate: { + ...caseToUpdate, + caseCaption: 'Iola Snow & Linda Singleton, Petitioners', + }, + }); + + expect( + applicationContext.getPersistenceGateway().saveWorkItemForPaper, + ).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().saveWorkItemForPaper.mock + .calls[0][0].workItem, + ).toMatchObject({ + assigneeId: petitionsClerkUser.userId, + assigneeName: petitionsClerkUser.name, + caseIsInProgress: true, + }); + }); + + it('should not update work items if the case is paper', async () => { + const caseToUpdate = Object.assign(MOCK_CASE); + caseToUpdate.isPaper = true; + caseToUpdate.mailingDate = 'yesterday'; + + await saveCaseDetailInternalEditInteractor({ + applicationContext, + caseId: caseToUpdate.caseId, + caseToUpdate: { + ...caseToUpdate, + caseCaption: 'Iola Snow & Linda Singleton, Petitioners', + }, + }); + + expect( + applicationContext.getPersistenceGateway().saveWorkItemForPaper, + ).not.toBeCalled(); + }); + it('should fail if the primary or secondary contact is empty', async () => { const caseToUpdate = Object.assign(MOCK_CASE); - caseToUpdate.documents = MOCK_DOCUMENTS; await expect( saveCaseDetailInternalEditInteractor({ diff --git a/shared/src/business/useCases/saveSignedDocumentInteractor.js b/shared/src/business/useCases/saveSignedDocumentInteractor.js index b86682cb03d..654e676459b 100644 --- a/shared/src/business/useCases/saveSignedDocumentInteractor.js +++ b/shared/src/business/useCases/saveSignedDocumentInteractor.js @@ -1,5 +1,6 @@ const { Case } = require('../entities/cases/Case'); const { Document } = require('../entities/Document'); +const { SIGNED_DOCUMENT_TYPES } = require('../entities/EntityConstants'); /** * saveSignedDocumentInteractor @@ -41,11 +42,10 @@ exports.saveSignedDocumentInteractor = async ({ createdAt: applicationContext.getUtilities().createISODateString(), documentId: signedDocumentId, documentTitle: - Document.SIGNED_DOCUMENT_TYPES.signedStipulatedDecision.documentType, + SIGNED_DOCUMENT_TYPES.signedStipulatedDecision.documentType, documentType: - Document.SIGNED_DOCUMENT_TYPES.signedStipulatedDecision.documentType, - eventCode: - Document.SIGNED_DOCUMENT_TYPES.signedStipulatedDecision.eventCode, + SIGNED_DOCUMENT_TYPES.signedStipulatedDecision.documentType, + eventCode: SIGNED_DOCUMENT_TYPES.signedStipulatedDecision.eventCode, filedBy: originalDocumentEntity.filedBy, isPaper: false, processingStatus: 'complete', diff --git a/shared/src/business/useCases/sealCaseInteractor.test.js b/shared/src/business/useCases/sealCaseInteractor.test.js index 86758ab1938..0418b0e076f 100644 --- a/shared/src/business/useCases/sealCaseInteractor.test.js +++ b/shared/src/business/useCases/sealCaseInteractor.test.js @@ -1,7 +1,7 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../test/mockCase'); +const { ROLES } = require('../entities/EntityConstants'); const { sealCaseInteractor } = require('./sealCaseInteractor'); -const { User } = require('../entities/User'); describe('sealCaseInteractor', () => { beforeAll(() => { @@ -23,7 +23,7 @@ describe('sealCaseInteractor', () => { it('should call updateCase with the sealedDate set on the case and return the updated case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketClerk', }); diff --git a/shared/src/business/useCases/serveCaseToIrs/addDocketEntryForPaymentStatus.test.js b/shared/src/business/useCases/serveCaseToIrs/addDocketEntryForPaymentStatus.test.js deleted file mode 100644 index 959ecc5c51a..00000000000 --- a/shared/src/business/useCases/serveCaseToIrs/addDocketEntryForPaymentStatus.test.js +++ /dev/null @@ -1,46 +0,0 @@ -import { Case } from '../../entities/cases/Case'; -import { MOCK_CASE } from '../../../test/mockCase'; -import { addDocketEntryForPaymentStatus } from './serveCaseToIrsInteractor'; -import { applicationContext } from '../../test/createTestApplicationContext'; - -describe('addDocketEntryForPaymentStatus', () => { - it('adds a docketRecord for a paid petition payment', async () => { - const caseEntity = new Case( - { - ...MOCK_CASE, - petitionPaymentDate: 'Today', - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, - }, - { applicationContext }, - ); - await addDocketEntryForPaymentStatus({ applicationContext, caseEntity }); - - const addedDocketRecord = caseEntity.docketRecord.find( - docketEntry => docketEntry.eventCode === 'FEE', - ); - - expect(addedDocketRecord).toBeDefined(); - expect(addedDocketRecord.filingDate).toEqual('Today'); - }); - - it('adds a docketRecord for a waived petition payment', async () => { - const caseEntity = new Case( - { - ...MOCK_CASE, - contactPrimary: undefined, - documents: [], - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, - petitionPaymentWaivedDate: 'Today', - }, - { applicationContext }, - ); - await addDocketEntryForPaymentStatus({ applicationContext, caseEntity }); - - const addedDocketRecord = caseEntity.docketRecord.find( - docketEntry => docketEntry.eventCode === 'FEEW', - ); - - expect(addedDocketRecord).toBeDefined(); - expect(addedDocketRecord.filingDate).toEqual('Today'); - }); -}); diff --git a/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.js b/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.js index 0d4bde90b3a..2ce2da5d992 100644 --- a/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.js +++ b/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.js @@ -1,18 +1,22 @@ +const { + INITIAL_DOCUMENT_TYPES, + PAYMENT_STATUS, +} = require('../../entities/EntityConstants'); const { isAuthorized, ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); const { Case } = require('../../entities/cases/Case'); const { DocketRecord } = require('../../entities/DocketRecord'); -const { Document } = require('../../entities/Document'); -const { PETITIONS_SECTION } = require('../../entities/WorkQueue'); +const { getCaseCaptionMeta } = require('../../utilities/getCaseCaptionMeta'); +const { PETITIONS_SECTION } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); exports.addDocketEntryForPaymentStatus = ({ applicationContext, caseEntity, }) => { - if (caseEntity.petitionPaymentStatus === Case.PAYMENT_STATUS.PAID) { + if (caseEntity.petitionPaymentStatus === PAYMENT_STATUS.PAID) { caseEntity.addDocketRecord( new DocketRecord( { @@ -23,7 +27,7 @@ exports.addDocketEntryForPaymentStatus = ({ { applicationContext }, ), ); - } else if (caseEntity.petitionPaymentStatus === Case.PAYMENT_STATUS.WAIVED) { + } else if (caseEntity.petitionPaymentStatus === PAYMENT_STATUS.WAIVED) { caseEntity.addDocketRecord( new DocketRecord( { @@ -40,8 +44,7 @@ exports.addDocketEntryForPaymentStatus = ({ exports.deleteStinIfAvailable = async ({ applicationContext, caseEntity }) => { const stinDocument = caseEntity.documents.find( document => - document.documentType === - Document.INITIAL_DOCUMENT_TYPES.stin.documentType, + document.documentType === INITIAL_DOCUMENT_TYPES.stin.documentType, ); if (stinDocument) { @@ -60,6 +63,7 @@ exports.deleteStinIfAvailable = async ({ applicationContext, caseEntity }) => { * @param {object} providers the providers object * @param {object} providers.applicationContext the application context * @param {string} providers.caseId the id of the case + * @returns {Buffer} paper service pdf if the case is a paper case */ exports.serveCaseToIrsInteractor = async ({ applicationContext, caseId }) => { const user = applicationContext.getCurrentUser(); @@ -77,11 +81,10 @@ exports.serveCaseToIrsInteractor = async ({ applicationContext, caseId }) => { const caseEntity = new Case(caseToBatch, { applicationContext }); - for (const initialDocumentTypeKey of Object.keys( - Document.INITIAL_DOCUMENT_TYPES, - )) { - const initialDocumentType = - Document.INITIAL_DOCUMENT_TYPES[initialDocumentTypeKey]; + caseEntity.markAsSentToIRS(); + + for (const initialDocumentTypeKey of Object.keys(INITIAL_DOCUMENT_TYPES)) { + const initialDocumentType = INITIAL_DOCUMENT_TYPES[initialDocumentTypeKey]; const initialDocument = caseEntity.documents.find( document => document.documentType === initialDocumentType.documentType, @@ -98,7 +101,7 @@ exports.serveCaseToIrsInteractor = async ({ applicationContext, caseId }) => { if ( initialDocument.documentType === - Document.INITIAL_DOCUMENT_TYPES.petition.documentType + INITIAL_DOCUMENT_TYPES.petition.documentType ) { await applicationContext .getUseCaseHelpers() @@ -137,18 +140,20 @@ exports.serveCaseToIrsInteractor = async ({ applicationContext, caseId }) => { // item => item.documentId !== deletedStinDocumentId, // ); - caseEntity.markAsSentToIRS(); - const petitionDocument = caseEntity.documents.find( document => - document.documentType === - Document.INITIAL_DOCUMENT_TYPES.petition.documentType, + document.documentType === INITIAL_DOCUMENT_TYPES.petition.documentType, ); const initializeCaseWorkItem = petitionDocument.workItems.find( workItem => workItem.isInitializeCase, ); + initializeCaseWorkItem.document.servedAt = petitionDocument.servedAt; + initializeCaseWorkItem.caseTitle = Case.getCaseTitle(caseEntity.caseCaption); + initializeCaseWorkItem.docketNumberWithSuffix = + caseEntity.docketNumberWithSuffix; + await applicationContext.getPersistenceGateway().deleteWorkItemFromInbox({ applicationContext, workItem: initializeCaseWorkItem.validate().toRawObject(), @@ -181,19 +186,59 @@ exports.serveCaseToIrsInteractor = async ({ applicationContext, caseId }) => { applicationContext, caseId: caseEntity.caseId, documentId: doc.documentId, + replaceCoversheet: !caseEntity.isPaper, + useInitialData: !caseEntity.isPaper, }); } + const { caseCaptionExtension, caseTitle } = getCaseCaptionMeta(caseEntity); + const { docketNumberWithSuffix, preferredTrialCity, receivedAt } = caseEntity; + + const address = { + ...caseEntity.contactPrimary, + countryName: + caseEntity.contactPrimary.countryType !== 'domestic' + ? caseEntity.contactPrimary.country + : '', + }; + const pdfData = await applicationContext - .getUseCaseHelpers() - .generateCaseConfirmationPdf({ + .getDocumentGenerators() + .noticeOfReceiptOfPetition({ applicationContext, - caseEntity, + data: { + address, + caseCaptionExtension, + caseTitle, + docketNumberWithSuffix, + preferredTrialCity, + receivedAtFormatted: applicationContext + .getUtilities() + .formatDateString(receivedAt, 'MMMM D, YYYY'), + servedDate: applicationContext + .getUtilities() + .formatDateString(caseEntity.getIrsSendDate(), 'MMMM D, YYYY'), + }, }); + const caseConfirmationPdfName = caseEntity.getCaseConfirmationGeneratedPdfFileName(); + + await new Promise(resolve => { + const documentsBucket = applicationContext.getDocumentsBucketName(); + const s3Client = applicationContext.getStorageClient(); + + const params = { + Body: pdfData, + Bucket: documentsBucket, + ContentType: 'application/pdf', + Key: caseConfirmationPdfName, + }; + + s3Client.upload(params, resolve); + }); + if (caseEntity.isPaper) { const paperServicePdfBuffer = Buffer.from(pdfData); - return paperServicePdfBuffer; } }; diff --git a/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.test.js b/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.test.js index 1073ac8e354..93395193e5c 100644 --- a/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.test.js +++ b/shared/src/business/useCases/serveCaseToIrs/serveCaseToIrsInteractor.test.js @@ -1,12 +1,18 @@ -const fs = require('fs'); -const path = require('path'); +const { + addDocketEntryForPaymentStatus, + serveCaseToIrsInteractor, +} = require('./serveCaseToIrsInteractor'); const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { + CASE_STATUS_TYPES, + INITIAL_DOCUMENT_TYPES, + PAYMENT_STATUS, +} = require('../../entities/EntityConstants'); const { Case } = require('../../entities/cases/Case'); -const { Document } = require('../../entities/Document'); const { MOCK_CASE } = require('../../../test/mockCase'); -const { serveCaseToIrsInteractor } = require('./serveCaseToIrsInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); describe('serveCaseToIrsInteractor', () => { @@ -15,7 +21,7 @@ describe('serveCaseToIrsInteractor', () => { assigneeId: null, assigneeName: 'IRSBatchSystem', caseId: 'e631d81f-a579-4de5-b8a8-b3f10ef619fd', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, completedAt: '2018-12-27T18:06:02.968Z', completedBy: 'Petitioner', completedByUserId: '6805d1ab-18d0-43ec-bafb-654e83405416', @@ -25,7 +31,7 @@ describe('serveCaseToIrsInteractor', () => { document: { createdAt: '2018-12-27T18:06:02.968Z', documentId: 'b6238482-5f0e-48a8-bb8e-da2957074a08', - documentType: Document.INITIAL_DOCUMENT_TYPES.petition.documentType, + documentType: INITIAL_DOCUMENT_TYPES.petition.documentType, }, isInitializeCase: true, isQC: true, @@ -36,54 +42,34 @@ describe('serveCaseToIrsInteractor', () => { fromUserId: '6805d1ab-18d0-43ec-bafb-654e83405416', message: 'Petition ready for review', messageId: '343f5b21-a3a9-4657-8e2b-df782f920e45', - role: User.ROLES.petitioner, + role: ROLES.petitioner, to: null, }, ], - section: 'irsBatchSection', + section: 'docket', sentBy: 'petitioner', updatedAt: '2018-12-27T18:06:02.968Z', workItemId: '78de1ba3-add3-4329-8372-ce37bda6bc93', }, ]; - const testAssetsPath = path.join(__dirname, '../../../../test-assets/'); - - const testPdfDocBytes = () => { - return new Uint8Array(fs.readFileSync(testAssetsPath + 'sample.pdf')); - }; - - const testPdfDoc = testPdfDocBytes(); - - const MOCK_PDF_DATA = - 'JVBERi0xLjcKJYGBgYEKCjUgMCBvYmoKPDwKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL0xlbm' + - 'd0aCAxMDQKPj4Kc3RyZWFtCniccwrhMlAAwaJ0Ln2P1Jyy1JLM5ERdc0MjCwUjE4WQNC4Q' + - '6cNlCFZkqGCqYGSqEJLLZWNuYGZiZmbkYuZsZmlmZGRgZmluDCQNzc3NTM2NzdzMXMxMjQ' + - 'ztFEKyuEK0uFxDuAAOERdVCmVuZHN0cmVhbQplbmRvYmoKCjYgMCBvYmoKPDwKL0ZpbHRl' + - 'ciAvRmxhdGVEZWNvZGUKL1R5cGUgL09ialN0bQovTiA0Ci9GaXJzdCAyMAovTGVuZ3RoID' + - 'IxNQo+PgpzdHJlYW0KeJxVj9GqwjAMhu/zFHkBzTo3nCCCiiKIHPEICuJF3cKoSCu2E8/b' + - '20wPIr1p8v9/8kVhgilmGfawX2CGaVrgcAi0/bsy0lrX7IGWpvJ4iJYEN3gEmrrGBlQwGs' + - 'HHO9VBX1wNrxAqMX87RBD5xpJuddqwd82tjAHxzV1U5LPgy52DKXWnr1Lheg+j/c/pzGVr' + - 'iqV0VlwZPXGPCJjElw/ybkwUmeoWgxesDXGhHJC/D/iikp1Av80ptKU0FdBEe25pPihAM1' + - 'u6ytgaaWfs2Hrz35CJT1+EWmAKZW5kc3RyZWFtCmVuZG9iagoKNyAwIG9iago8PAovU2l6' + - 'ZSA4Ci9Sb290IDIgMCBSCi9GaWx0ZXIgL0ZsYXRlRGVjb2RlCi9UeXBlIC9YUmVmCi9MZW' + - '5ndGggMzgKL1cgWyAxIDIgMiBdCi9JbmRleCBbIDAgOCBdCj4+CnN0cmVhbQp4nBXEwREA' + - 'EBAEsCwz3vrvRmOOyyOoGhZdutHN2MT55fIAVocD+AplbmRzdHJlYW0KZW5kb2JqCgpzdG' + - 'FydHhyZWYKNTEwCiUlRU9G'; - let mockCase; beforeAll(() => { mockCase = MOCK_CASE; mockCase.documents[0].workItems = MOCK_WORK_ITEMS; - applicationContext - .getUseCaseHelpers() - .generatePaperServiceAddressPagePdf.mockResolvedValue(testPdfDoc); + applicationContext.getPersistenceGateway().updateWorkItem = jest.fn(); + + applicationContext.getStorageClient.mockReturnValue({ + upload: (params, cb) => { + return cb(true); + }, + }); }); it('should throw unauthorized error when user is unauthorized', async () => { applicationContext.getCurrentUser.mockReturnValue({ - userId: 'notauser', + userId: 'b88a8284-b859-4641-a270-b3ee26c6c068', }); await expect( @@ -103,16 +89,40 @@ describe('serveCaseToIrsInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'bob', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); applicationContext .getPersistenceGateway() .getCaseByCaseId.mockReturnValue(mockCase); + + await serveCaseToIrsInteractor({ + applicationContext, + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + }); + + expect( + applicationContext.getUseCases().addCoversheetInteractor, + ).toHaveBeenCalled(); + expect( + applicationContext.getUseCases().addCoversheetInteractor.mock.calls[0][0], + ).toMatchObject({ + replaceCoversheet: false, + }); + }); + + it('should replace coversheet on the served petition if the case is not paper', async () => { + applicationContext.getCurrentUser.mockReturnValue( + new User({ + name: 'bob', + role: ROLES.petitionsClerk, + userId: '6805d1ab-18d0-43ec-bafb-654e83405416', + }), + ); applicationContext - .getUseCaseHelpers() - .generateCaseConfirmationPdf.mockReturnValue(MOCK_PDF_DATA); + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(MOCK_CASE); await serveCaseToIrsInteractor({ applicationContext, @@ -122,6 +132,66 @@ describe('serveCaseToIrsInteractor', () => { expect( applicationContext.getUseCases().addCoversheetInteractor, ).toHaveBeenCalled(); + expect( + applicationContext.getUseCases().addCoversheetInteractor.mock.calls[0][0], + ).toMatchObject({ + replaceCoversheet: true, + }); + }); + + it('should preserve original case caption and docket number on the coversheet if the case is not paper', async () => { + applicationContext.getCurrentUser.mockReturnValue( + new User({ + name: 'bob', + role: ROLES.petitionsClerk, + userId: '6805d1ab-18d0-43ec-bafb-654e83405416', + }), + ); + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(MOCK_CASE); + + await serveCaseToIrsInteractor({ + applicationContext, + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + }); + + expect( + applicationContext.getUseCases().addCoversheetInteractor, + ).toHaveBeenCalled(); + expect( + applicationContext.getUseCases().addCoversheetInteractor.mock.calls[0][0], + ).toMatchObject({ + replaceCoversheet: true, + useInitialData: true, + }); + }); + + it('should generate a notice of receipt of petition document and upload it to s3', async () => { + mockCase = { + ...MOCK_CASE, + isPaper: false, + }; + applicationContext.getCurrentUser.mockReturnValue( + new User({ + name: 'bob', + role: ROLES.petitionsClerk, + userId: '6805d1ab-18d0-43ec-bafb-654e83405416', + }), + ); + applicationContext + .getPersistenceGateway() + .getCaseByCaseId.mockReturnValue(mockCase); + + await serveCaseToIrsInteractor({ + applicationContext, + caseId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', + }); + + expect( + applicationContext.getDocumentGenerators().noticeOfReceiptOfPetition, + ).toHaveBeenCalled(); + expect(applicationContext.getStorageClient).toHaveBeenCalled(); }); it('should not return a paper service pdf when the case is electronic', async () => { @@ -132,7 +202,7 @@ describe('serveCaseToIrsInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'bob', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -157,7 +227,7 @@ describe('serveCaseToIrsInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'bob', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -186,7 +256,7 @@ describe('serveCaseToIrsInteractor', () => { documentType: 'Request for Place of Trial', eventCode: 'RPT', processingStatus: 'pending', - userId: 'petitioner', + userId: 'b88a8284-b859-4641-a270-b3ee26c6c068', workItems: [], }, { @@ -197,7 +267,7 @@ describe('serveCaseToIrsInteractor', () => { documentType: 'Application for Waiver of Filing Fee', eventCode: 'APW', processingStatus: 'pending', - userId: 'petitioner', + userId: 'b88a8284-b859-4641-a270-b3ee26c6c068', workItems: [], }, ], @@ -207,7 +277,7 @@ describe('serveCaseToIrsInteractor', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'bob', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -225,7 +295,7 @@ describe('serveCaseToIrsInteractor', () => { .updateCase.mock.calls[0][0].caseToUpdate.documents.find( document => document.documentType === - Document.INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.documentType, + INITIAL_DOCUMENT_TYPES.requestForPlaceOfTrial.documentType, ); expect(result).toBeDefined(); expect(documentWithServedParties.servedParties).toBeDefined(); @@ -235,5 +305,54 @@ describe('serveCaseToIrsInteractor', () => { expect( applicationContext.getUseCaseHelpers().sendServedPartiesEmails, ).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().updateWorkItem, + ).toBeCalled(); + expect( + applicationContext.getPersistenceGateway().updateWorkItem.mock.calls[0][0] + .workItemToUpdate.document.servedAt, + ).toBeDefined(); + }); +}); + +describe('addDocketEntryForPaymentStatus', () => { + it('adds a docketRecord for a paid petition payment', async () => { + const caseEntity = new Case( + { + ...MOCK_CASE, + petitionPaymentDate: 'Today', + petitionPaymentStatus: PAYMENT_STATUS.PAID, + }, + { applicationContext }, + ); + await addDocketEntryForPaymentStatus({ applicationContext, caseEntity }); + + const addedDocketRecord = caseEntity.docketRecord.find( + docketEntry => docketEntry.eventCode === 'FEE', + ); + + expect(addedDocketRecord).toBeDefined(); + expect(addedDocketRecord.filingDate).toEqual('Today'); + }); + + it('adds a docketRecord for a waived petition payment', async () => { + const caseEntity = new Case( + { + ...MOCK_CASE, + contactPrimary: undefined, + documents: [], + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, + petitionPaymentWaivedDate: 'Today', + }, + { applicationContext }, + ); + await addDocketEntryForPaymentStatus({ applicationContext, caseEntity }); + + const addedDocketRecord = caseEntity.docketRecord.find( + docketEntry => docketEntry.eventCode === 'FEEW', + ); + + expect(addedDocketRecord).toBeDefined(); + expect(addedDocketRecord.filingDate).toEqual('Today'); }); }); diff --git a/shared/src/business/useCases/trialSessions/addCaseToTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/addCaseToTrialSessionInteractor.test.js index 96f6855a46f..9f0b9b2c645 100644 --- a/shared/src/business/useCases/trialSessions/addCaseToTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/addCaseToTrialSessionInteractor.test.js @@ -1,7 +1,6 @@ import { addCaseToTrialSessionInteractor } from './addCaseToTrialSessionInteractor'; -const { Case } = require('../../entities/cases/Case'); +const { CHIEF_JUDGE, ROLES } = require('../../entities/EntityConstants'); const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); const MOCK_TRIAL = { maxCases: 100, @@ -19,7 +18,7 @@ describe('addCaseToTrialSessionInteractor', () => { await addCaseToTrialSessionInteractor({ applicationContext: { getCurrentUser: () => ({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '8675309b-18d0-43ec-bafb-654e83405411', }), getPersistenceGateway: () => ({ @@ -45,7 +44,7 @@ describe('addCaseToTrialSessionInteractor', () => { await addCaseToTrialSessionInteractor({ applicationContext: { getCurrentUser: () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '8675309b-18d0-43ec-bafb-654e83405411', }), getPersistenceGateway: () => ({ @@ -75,7 +74,7 @@ describe('addCaseToTrialSessionInteractor', () => { await addCaseToTrialSessionInteractor({ applicationContext: { getCurrentUser: () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '8675309b-18d0-43ec-bafb-654e83405411', }), getPersistenceGateway: () => ({ @@ -107,7 +106,7 @@ describe('addCaseToTrialSessionInteractor', () => { const latestCase = await addCaseToTrialSessionInteractor({ applicationContext: { getCurrentUser: () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '8675309b-18d0-43ec-bafb-654e83405411', }), getPersistenceGateway: () => ({ @@ -129,7 +128,7 @@ describe('addCaseToTrialSessionInteractor', () => { }); expect(latestCase).toMatchObject({ - associatedJudge: Case.CHIEF_JUDGE, + associatedJudge: CHIEF_JUDGE, status: 'Calendared', trialDate: '2025-12-01T00:00:00.000Z', trialLocation: 'Birmingham, Alabama', @@ -144,7 +143,7 @@ describe('addCaseToTrialSessionInteractor', () => { await addCaseToTrialSessionInteractor({ applicationContext: { getCurrentUser: () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '8675309b-18d0-43ec-bafb-654e83405411', }), getPersistenceGateway: () => ({ diff --git a/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.js b/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.js index d677c58c1ff..8fbbfb0dd65 100644 --- a/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.js +++ b/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.js @@ -4,6 +4,7 @@ const { ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { formatDateString } = require('../../../business/utilities/DateHandler'); const { padStart } = require('lodash'); const { UnauthorizedError } = require('../../../errors/errors'); @@ -55,7 +56,7 @@ const batchDownloadTrialSessionInteractor = async ({ .replace(/,/g, ''); // TODO - create a sanitize utility for s3 ids // TODO - should we make these unique somehow? sessionCases = sessionCases - .filter(caseToFilter => caseToFilter.status !== Case.STATUS_TYPES.closed) + .filter(caseToFilter => caseToFilter.status !== CASE_STATUS_TYPES.closed) .map(caseToBatch => { const caseTitle = Case.getCaseTitle(caseToBatch.caseCaption); const caseFolder = `${caseToBatch.docketNumber}, ${caseTitle}`; diff --git a/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.test.js index f5254d6a6fd..00511fd5ba6 100644 --- a/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/batchDownloadTrialSessionInteractor.test.js @@ -2,9 +2,9 @@ import { batchDownloadTrialSessionInteractor } from './batchDownloadTrialSession const { applicationContext, } = require('../../test/createTestApplicationContext'); -const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('batchDownloadTrialSessionInteractor', () => { let user; @@ -26,7 +26,7 @@ describe('batchDownloadTrialSessionInteractor', () => { ]; user = { - role: User.ROLES.judge, + role: ROLES.judge, userId: 'abc-123', }; applicationContext.getCurrentUser.mockImplementation(() => user); @@ -56,7 +56,7 @@ describe('batchDownloadTrialSessionInteractor', () => { it('throws an Unauthorized error if the user role is not allowed to access the method', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'abc-123', }; @@ -105,7 +105,7 @@ describe('batchDownloadTrialSessionInteractor', () => { .getCalendaredCasesForTrialSession.mockReturnValue([ { ...MOCK_CASE, - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, ]); diff --git a/shared/src/business/useCases/trialSessions/canSetTrialSessionAsCalendaredInteractor.test.js b/shared/src/business/useCases/trialSessions/canSetTrialSessionAsCalendaredInteractor.test.js index 5f230d796ec..9fdf5470192 100644 --- a/shared/src/business/useCases/trialSessions/canSetTrialSessionAsCalendaredInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/canSetTrialSessionAsCalendaredInteractor.test.js @@ -4,7 +4,7 @@ const { const { canSetTrialSessionAsCalendaredInteractor, } = require('./canSetTrialSessionAsCalendaredInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const MOCK_TRIAL = { maxCases: 100, @@ -45,7 +45,7 @@ describe('canSetTrialSessionAsCalendaredInteractor', () => { it('gets the result back from the interactor', () => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; diff --git a/shared/src/business/useCases/trialSessions/createTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/createTrialSessionInteractor.test.js index 10fb8233828..92a68e22122 100644 --- a/shared/src/business/useCases/trialSessions/createTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/createTrialSessionInteractor.test.js @@ -4,6 +4,7 @@ const { const { createTrialSessionInteractor, } = require('./createTrialSessionInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); const MOCK_TRIAL = { @@ -21,7 +22,7 @@ describe('createTrialSessionInteractor', () => { beforeEach(() => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -39,7 +40,7 @@ describe('createTrialSessionInteractor', () => { it('throws error if user is unauthorized', async () => { user = new User({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); diff --git a/shared/src/business/useCases/trialSessions/deleteTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/deleteTrialSessionInteractor.test.js index 31f82b09e8a..e68bfd8a549 100644 --- a/shared/src/business/useCases/trialSessions/deleteTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/deleteTrialSessionInteractor.test.js @@ -4,6 +4,7 @@ const { const { deleteTrialSessionInteractor, } = require('./deleteTrialSessionInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); const { MOCK_CASE } = require('../../../test/mockCase'); @@ -40,7 +41,7 @@ describe('deleteTrialSessionInteractor', () => { it('throws error if user is unauthorized', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; @@ -55,7 +56,7 @@ describe('deleteTrialSessionInteractor', () => { it('throws an exception when it fails to find a trial session', async () => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -72,7 +73,7 @@ describe('deleteTrialSessionInteractor', () => { it('throws error when trial session start date is in the past', async () => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -91,7 +92,7 @@ describe('deleteTrialSessionInteractor', () => { it('throws error if trial session is calendared', async () => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -112,7 +113,7 @@ describe('deleteTrialSessionInteractor', () => { it('deletes the trial session and invokes expected persistence methods', async () => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); diff --git a/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.js b/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.js index e238b5ebc03..201c0bc2733 100644 --- a/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.js +++ b/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.js @@ -1,3 +1,9 @@ +const { + createISODateString, + formatDateString, +} = require('../../utilities/DateHandler'); +const { getCaseCaptionMeta } = require('../../utilities/getCaseCaptionMeta'); + /** * generateStandingPretrialNoticeInteractor * @@ -26,47 +32,42 @@ exports.generateStandingPretrialNoticeInteractor = async ({ docketNumber, }); - const { - address1, - address2, - city, - courthouseName, - judge, - postalCode, - startDate, - startTime, - state, - } = trialSession; + const { docketNumberWithSuffix, irsPractitioners } = caseDetail; + + let respondentContactText = 'not available at this time'; + if (irsPractitioners && irsPractitioners.length) { + const firstRespondent = irsPractitioners[0]; + respondentContactText = `${firstRespondent.name} (${firstRespondent.contact.phone})`; + } - const { caseCaption, docketNumberWithSuffix, irsPractitioners } = caseDetail; + const trialStartTimeIso = createISODateString( + trialSession.startTime, + 'HH:mm', + ); + const startTime = formatDateString(trialStartTimeIso, 'hh:mm A'); + const startDay = formatDateString(trialSession.startDate, 'dddd'); + const fullStartDate = formatDateString( + trialSession.startDate, + 'dddd, MMMM D, YYYY', + ); - const contentHtml = await applicationContext - .getTemplateGenerators() - .generateStandingPretrialNoticeTemplate({ + const { caseCaptionExtension, caseTitle } = getCaseCaptionMeta(caseDetail); + + return await applicationContext + .getDocumentGenerators() + .standingPretrialNotice({ applicationContext, - content: { - caseCaption, + data: { + caseCaptionExtension, + caseTitle, docketNumberWithSuffix, trialInfo: { - address1, - address2, - city, - courthouseName, - irsPractitioners, - judge, - postalCode, - startDate, + ...trialSession, + fullStartDate, + respondentContactText, + startDay, startTime, - state, }, }, }); - - return await applicationContext.getUseCases().generatePdfFromHtmlInteractor({ - applicationContext, - contentHtml, - headerHtml: - '
', - overwriteHeader: true, - }); }; diff --git a/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.test.js b/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.test.js index 9a74eb9e769..9bdd9f6a37c 100644 --- a/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor.test.js @@ -13,12 +13,6 @@ describe('generateStandingPretrialNoticeInteractor', () => { ({ contentHtml }) => contentHtml, ); - applicationContext - .getTemplateGenerators() - .generateStandingPretrialNoticeTemplate.mockImplementation( - ({ content }) => `${content.docketNumberWithSuffix}`, - ); - applicationContext .getPersistenceGateway() .getCaseByDocketNumber.mockImplementation(({ docketNumber }) => { @@ -26,7 +20,9 @@ describe('generateStandingPretrialNoticeInteractor', () => { return { caseCaption: 'Test Case Caption', docketNumber: '123-45', + docketNumberSuffix: '', docketNumberWithSuffix: '123-45', + irsPractitioners: [], }; } else { return { @@ -34,6 +30,12 @@ describe('generateStandingPretrialNoticeInteractor', () => { docketNumber: '234-56', docketNumberSuffix: 'S', docketNumberWithSuffix: '234-56S', + irsPractitioners: [ + { + contact: { phone: '123-123-1234' }, + name: 'Test IRS Practitioner', + }, + ], }; } }); @@ -47,13 +49,14 @@ describe('generateStandingPretrialNoticeInteractor', () => { courthouseName: 'Courthouse 1', judge: 'Test Judge', postalCode: '12345', - startDate: '2/2/2020', - startTime: '10:00', + startDate: '2020-02-03T09:00:00.000Z', + startTime: '09:00', state: 'ST', }); }); - it('should generate a template with the case and trial information and call the pdf generator', async () => { - const result = await generateStandingPretrialNoticeInteractor({ + + it('should fetch case and trial information and call the document generator', async () => { + await generateStandingPretrialNoticeInteractor({ applicationContext, docketNumber: '123-45', trialSessionId: '959c4338-0fac-42eb-b0eb-d53b8d0195cc', @@ -66,35 +69,61 @@ describe('generateStandingPretrialNoticeInteractor', () => { applicationContext.getPersistenceGateway().getCaseByDocketNumber, ).toHaveBeenCalled(); expect( - applicationContext.getTemplateGenerators() - .generateStandingPretrialNoticeTemplate, + applicationContext.getDocumentGenerators().standingPretrialNotice, ).toHaveBeenCalled(); - expect( - applicationContext.getUseCases().generatePdfFromHtmlInteractor, - ).toHaveBeenCalled(); - expect(result.indexOf('123-45')).toBeGreaterThan(-1); }); it('should append the docket number suffix if present on the caseDetail', async () => { - const result = await generateStandingPretrialNoticeInteractor({ + await generateStandingPretrialNoticeInteractor({ applicationContext, docketNumber: '234-56', trialSessionId: '959c4338-0fac-42eb-b0eb-d53b8d0195cc', }); + const { + data, + } = applicationContext.getDocumentGenerators().standingPretrialNotice.mock.calls[0][0]; + + expect(data.docketNumberWithSuffix).toEqual('234-56S'); + }); + + it('return the respondent contact info if present', async () => { + await generateStandingPretrialNoticeInteractor({ + applicationContext, + docketNumber: '123-45', + trialSessionId: '959c4338-0fac-42eb-b0eb-d53b8d0195cc', + }); + expect( - applicationContext.getPersistenceGateway().getTrialSessionById, - ).toHaveBeenCalled(); - expect( - applicationContext.getPersistenceGateway().getCaseByDocketNumber, - ).toHaveBeenCalled(); - expect( - applicationContext.getTemplateGenerators() - .generateStandingPretrialNoticeTemplate, - ).toHaveBeenCalled(); + applicationContext.getDocumentGenerators().standingPretrialNotice.mock + .calls[0][0].data.trialInfo.respondentContactText, + ).toEqual('not available at this time'); + + await generateStandingPretrialNoticeInteractor({ + applicationContext, + docketNumber: '234-56', + trialSessionId: '959c4338-0fac-42eb-b0eb-d53b8d0195cc', + }); + expect( - applicationContext.getUseCases().generatePdfFromHtmlInteractor, - ).toHaveBeenCalled(); - expect(result.indexOf('234-56S')).toBeGreaterThan(-1); + applicationContext.getDocumentGenerators().standingPretrialNotice.mock + .calls[1][0].data.trialInfo.respondentContactText, + ).toEqual('Test IRS Practitioner (123-123-1234)'); + }); + + it('should format trial start info', async () => { + await generateStandingPretrialNoticeInteractor({ + applicationContext, + docketNumber: '234-56', + trialSessionId: '959c4338-0fac-42eb-b0eb-d53b8d0195cc', + }); + + const { + data, + } = applicationContext.getDocumentGenerators().standingPretrialNotice.mock.calls[0][0]; + + expect(data.trialInfo.fullStartDate).toEqual('Monday, February 3, 2020'); + expect(data.trialInfo.startDay).toEqual('Monday'); + expect(data.trialInfo.startTime).toEqual('09:00 AM'); }); }); diff --git a/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.js b/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.js index d6d6e6798de..e448bacf4fd 100644 --- a/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.js +++ b/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.js @@ -35,29 +35,57 @@ exports.generateTrialCalendarPdfInteractor = async ({ trialSessionId, }); + const getPractitionerName = practitioner => { + const { barNumber, name } = practitioner; + const barNumberFormatted = barNumber ? ` (${barNumber})` : ''; + return `${name}${barNumberFormatted}`; + }; + const formattedOpenCases = openCases.map(openCase => { - return applicationContext.getUtilities().getFormattedCaseDetail({ - applicationContext, - caseDetail: openCase, - }); + return { + caseTitle: applicationContext.getCaseTitle(openCase.caseCaption || ''), + docketNumber: `${openCase.docketNumber}${ + openCase.docketNumberSuffix || '' + }`, + petitionerCounsel: (openCase.privatePractitioners || []).map( + getPractitionerName, + ), + respondentCounsel: (openCase.irsPractitioners || []).map( + getPractitionerName, + ), + }; }); - const contentHtml = await applicationContext - .getTemplateGenerators() - .generateTrialCalendarTemplate({ - applicationContext, - content: { - formattedTrialSessionDetails: formattedTrialSession, - openCases: formattedOpenCases, - }, - }); + const { + formattedCourtReporter, + formattedIrsCalendarAdministrator, + formattedJudge, + formattedStartDateFull, + formattedStartTime, + formattedTrialClerk, + } = formattedTrialSession; - const file = await applicationContext - .getUseCases() - .generatePdfFromHtmlInteractor({ - applicationContext, - contentHtml, - }); + const sessionDetail = { + ...formattedTrialSession, + courtReporter: formattedCourtReporter, + irsCalendarAdministrator: formattedIrsCalendarAdministrator, + judge: formattedJudge, + startDate: formattedStartDateFull, + startTime: formattedStartTime, + trialClerk: formattedTrialClerk, + }; - return await saveFileAndGenerateUrl({ applicationContext, file }); + const file = await applicationContext.getDocumentGenerators().trialCalendar({ + applicationContext, + data: { + cases: formattedOpenCases, + sessionDetail, + }, + }); + + return await saveFileAndGenerateUrl({ + applicationContext, + file, + useTempBucket: true, + }); }; diff --git a/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.test.js b/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.test.js index 665cc607c24..10d8aa31baa 100644 --- a/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/generateTrialCalendarPdfInteractor.test.js @@ -19,12 +19,31 @@ describe('generateTrialCalendarPdfInteractor', () => { ]); applicationContext - .getTemplateGenerators() - .generateTrialCalendarTemplate.mockReturnValue(true); + .getPersistenceGateway() + .getDownloadPolicyUrl.mockReturnValue(mockPdfUrl); applicationContext .getPersistenceGateway() - .getDownloadPolicyUrl.mockReturnValue(mockPdfUrl); + .getTrialSessionById.mockReturnValue({ + address1: '123 Some Street', + address2: 'Suite B', + city: 'New York', + courtReporter: 'Lois Lane', + courthouseName: 'Test Courthouse', + irsCalendarAdministrator: 'iCalRS Admin', + judge: { name: 'Joseph Dredd' }, + notes: + 'The one with the velour shirt is definitely looking at me funny.', + sessionType: 'Hybrid', + startDate: '2019-12-02T05:00:00.000Z', + startTime: '09:00', + state: 'NY', + term: 'Fall', + termYear: '2019', + trialClerk: 'Clerky McGee', + trialLocation: 'New York City, New York', + zip: '10108', + }); }); it('should find the cases for a trial session successfully', async () => { @@ -42,19 +61,15 @@ describe('generateTrialCalendarPdfInteractor', () => { .length, ).toBe(1); expect( - applicationContext.getUtilities().formattedTrialSessionDetails.mock.calls - .length, + applicationContext.getPersistenceGateway() + .getCalendaredCasesForTrialSession.mock.calls.length, ).toBe(1); expect( - applicationContext.getUtilities().getFormattedCaseDetail.mock.calls + applicationContext.getUtilities().formattedTrialSessionDetails.mock.calls .length, - ).toBe(3); - expect( - applicationContext.getTemplateGenerators().generateTrialCalendarTemplate - .mock.calls.length, ).toBe(1); expect( - applicationContext.getUseCases().generatePdfFromHtmlInteractor.mock.calls + applicationContext.getDocumentGenerators().trialCalendar.mock.calls .length, ).toBe(1); }); diff --git a/shared/src/business/useCases/trialSessions/getCalendaredCasesForTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/getCalendaredCasesForTrialSessionInteractor.test.js index 2216d0b55dc..7808b06a068 100644 --- a/shared/src/business/useCases/trialSessions/getCalendaredCasesForTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/getCalendaredCasesForTrialSessionInteractor.test.js @@ -4,6 +4,7 @@ const { const { getCalendaredCasesForTrialSessionInteractor, } = require('./getCalendaredCasesForTrialSessionInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -31,7 +32,7 @@ describe('getCalendaredCasesForTrialSessionInteractor', () => { it('throws an exception when the user is unauthorized', async () => { user = new User({ name: 'Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -46,7 +47,7 @@ describe('getCalendaredCasesForTrialSessionInteractor', () => { it('should find the cases for a trial session successfully', async () => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); diff --git a/shared/src/business/useCases/trialSessions/getEligibleCasesForTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/getEligibleCasesForTrialSessionInteractor.test.js index 1fee5d8d8c6..40461364e45 100644 --- a/shared/src/business/useCases/trialSessions/getEligibleCasesForTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/getEligibleCasesForTrialSessionInteractor.test.js @@ -4,6 +4,7 @@ const { const { getEligibleCasesForTrialSessionInteractor, } = require('./getEligibleCasesForTrialSessionInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); const { MOCK_CASE } = require('../../../test/mockCase'); @@ -43,7 +44,7 @@ describe('getEligibleCasesForTrialSessionInteractor', () => { () => new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -73,7 +74,7 @@ describe('getEligibleCasesForTrialSessionInteractor', () => { () => new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); diff --git a/shared/src/business/useCases/trialSessions/getTrialSessionDetailsInteractor.test.js b/shared/src/business/useCases/trialSessions/getTrialSessionDetailsInteractor.test.js index e30c337c75d..7c5d5050385 100644 --- a/shared/src/business/useCases/trialSessions/getTrialSessionDetailsInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/getTrialSessionDetailsInteractor.test.js @@ -5,8 +5,8 @@ const { getTrialSessionDetailsInteractor, } = require('./getTrialSessionDetailsInteractor'); const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('Get trial session details', () => { const MOCK_TRIAL_SESSION = { @@ -21,7 +21,7 @@ describe('Get trial session details', () => { beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }); }); diff --git a/shared/src/business/useCases/trialSessions/getTrialSessionWorkingCopyInteractor.test.js b/shared/src/business/useCases/trialSessions/getTrialSessionWorkingCopyInteractor.test.js index bc89b6e8677..450771854f8 100644 --- a/shared/src/business/useCases/trialSessions/getTrialSessionWorkingCopyInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/getTrialSessionWorkingCopyInteractor.test.js @@ -5,8 +5,8 @@ const { getTrialSessionWorkingCopyInteractor, } = require('./getTrialSessionWorkingCopyInteractor'); const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const MOCK_WORKING_COPY = { sort: 'practitioner', @@ -20,7 +20,7 @@ describe('Get trial session working copy', () => { beforeEach(() => { user = { - role: User.ROLES.judge, + role: ROLES.judge, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }; @@ -33,7 +33,7 @@ describe('Get trial session working copy', () => { applicationContext .getUseCases() .getJudgeForUserChambersInteractor.mockReturnValue({ - role: User.ROLES.judge, + role: ROLES.judge, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }); }); @@ -89,7 +89,7 @@ describe('Get trial session working copy', () => { it('correctly returns data from persistence for a trial clerk user', async () => { user = { - role: User.ROLES.trialClerk, + role: ROLES.trialClerk, userId: 'a9ae05ba-d48a-43a6-9981-ee536a7601be', }; applicationContext diff --git a/shared/src/business/useCases/trialSessions/getTrialSessionsInteractor.test.js b/shared/src/business/useCases/trialSessions/getTrialSessionsInteractor.test.js index f992e8400fa..fe11ffd8d89 100644 --- a/shared/src/business/useCases/trialSessions/getTrialSessionsInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/getTrialSessionsInteractor.test.js @@ -3,7 +3,7 @@ const { } = require('../../test/createTestApplicationContext'); const { getTrialSessionsInteractor } = require('./getTrialSessionsInteractor'); const { omit } = require('lodash'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const MOCK_TRIAL_SESSION = { maxCases: 100, @@ -29,7 +29,7 @@ describe('Get trial sessions', () => { it('throws an error if the entity returned from persistence is invalid', async () => { applicationContext.getCurrentUser.mockImplementation(() => { return { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; }); diff --git a/shared/src/business/useCases/trialSessions/removeCaseFromTrialInteractor.test.js b/shared/src/business/useCases/trialSessions/removeCaseFromTrialInteractor.test.js index e5c7ef17a23..5e09efa094f 100644 --- a/shared/src/business/useCases/trialSessions/removeCaseFromTrialInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/removeCaseFromTrialInteractor.test.js @@ -1,12 +1,15 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { + CASE_STATUS_TYPES, + CHIEF_JUDGE, +} = require('../../entities/EntityConstants'); const { removeCaseFromTrialInteractor, } = require('./removeCaseFromTrialInteractor'); -const { Case } = require('../../entities/cases/Case'); const { MOCK_CASE } = require('../../../test/mockCase'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('remove case from trial session', () => { const MOCK_TRIAL_SESSION = { @@ -28,7 +31,7 @@ describe('remove case from trial session', () => { beforeEach(() => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; @@ -51,7 +54,7 @@ describe('remove case from trial session', () => { it('throws error if user is unauthorized', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; mockTrialSession = MOCK_TRIAL_SESSION; @@ -120,9 +123,9 @@ describe('remove case from trial session', () => { applicationContext.getPersistenceGateway().updateCase.mock.calls[0][0] .caseToUpdate, ).toMatchObject({ - associatedJudge: Case.CHIEF_JUDGE, + associatedJudge: CHIEF_JUDGE, caseId: MOCK_CASE.caseId, - status: Case.STATUS_TYPES.generalDocketReadyForTrial, + status: CASE_STATUS_TYPES.generalDocketReadyForTrial, trialLocation: undefined, trialSessionId: undefined, }); @@ -175,9 +178,9 @@ describe('remove case from trial session', () => { applicationContext.getPersistenceGateway().updateCase.mock.calls[0][0] .caseToUpdate, ).toMatchObject({ - associatedJudge: Case.CHIEF_JUDGE, + associatedJudge: CHIEF_JUDGE, caseId: MOCK_CASE.caseId, - status: Case.STATUS_TYPES.generalDocketReadyForTrial, + status: CASE_STATUS_TYPES.generalDocketReadyForTrial, trialLocation: undefined, trialSessionId: undefined, }); diff --git a/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.js b/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.js index 512bcb5838b..994f1ad2b4b 100644 --- a/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.js +++ b/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.js @@ -2,9 +2,9 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); +const { capitalize } = require('lodash'); const { invert } = require('lodash'); -const { TrialSession } = require('../../entities/trialSessions/TrialSession'); +const { TRIAL_CITIES, US_STATES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const getPreviousTerm = (currentTerm, currentYear) => { @@ -38,7 +38,7 @@ const getTrialSessionPlanningReportData = async ({ currentYear = previous.year; } - const trialCities = [...TrialSession.TRIAL_CITIES.ALL]; + const trialCities = [...TRIAL_CITIES.ALL]; trialCities.sort((a, b) => { if (a.state === b.state) { return applicationContext.getUtilities().compareStrings(a.city, b.city); @@ -59,9 +59,7 @@ const getTrialSessionPlanningReportData = async ({ for (const trialLocation of trialCities) { const trialCityState = `${trialLocation.city}, ${trialLocation.state}`; const trialCityStateStripped = trialCityState.replace(/[\s.,]/g, ''); - const stateAbbreviation = invert(ContactFactory.US_STATES)[ - trialLocation.state - ]; + const stateAbbreviation = invert(US_STATES)[trialLocation.state]; const eligibleCasesSmall = await applicationContext .getPersistenceGateway() @@ -154,44 +152,22 @@ exports.runTrialSessionPlanningReportInteractor = async ({ year, }); - const contentHtml = await applicationContext - .getTemplateGenerators() - .generateTrialSessionPlanningReportTemplate({ + const trialSessionPlanningReport = await applicationContext + .getDocumentGenerators() + .trialSessionPlanningReport({ applicationContext, - content: { + data: { + locationData: reportData.trialLocationData, previousTerms: reportData.previousTerms, - rows: reportData.trialLocationData, - selectedTerm: term, - selectedYear: year, + term: `${capitalize(term)} ${year}`, }, }); - const pdf = await applicationContext - .getUseCases() - .generatePdfFromHtmlInteractor({ - applicationContext, - contentHtml, - headerHtml: ' ', - }); - - const trialSessionPlanningReportPdfId = applicationContext.getUniqueId(); - - await applicationContext.getPersistenceGateway().saveDocumentFromLambda({ + return await applicationContext.getUseCaseHelpers().saveFileAndGenerateUrl({ applicationContext, - document: pdf, - documentId: trialSessionPlanningReportPdfId, + file: trialSessionPlanningReport, useTempBucket: true, }); - - const trialSessionPlanningReportPdfUrl = await applicationContext - .getPersistenceGateway() - .getDownloadPolicyUrl({ - applicationContext, - documentId: trialSessionPlanningReportPdfId, - useTempBucket: true, - }); - - return trialSessionPlanningReportPdfUrl; }; exports.getPreviousTerm = getPreviousTerm; diff --git a/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.test.js b/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.test.js index b8a2347b65f..5a056c5df5a 100644 --- a/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/runTrialSessionPlanningReportInteractor.test.js @@ -6,8 +6,7 @@ const { getTrialSessionPlanningReportData, runTrialSessionPlanningReportInteractor, } = require('./runTrialSessionPlanningReportInteractor'); -const { TrialSession } = require('../../entities/trialSessions/TrialSession'); -const { User } = require('../../entities/User'); +const { ROLES, TRIAL_CITIES } = require('../../entities/EntityConstants'); describe('run trial session planning report', () => { const mockPdfUrl = 'www.example.com'; @@ -16,13 +15,13 @@ describe('run trial session planning report', () => { beforeEach(() => { applicationContext.getCurrentUser.mockImplementation(() => user); applicationContext - .getPersistenceGateway() - .getDownloadPolicyUrl.mockReturnValue(mockPdfUrl); + .getUseCaseHelpers() + .saveFileAndGenerateUrl.mockReturnValue(mockPdfUrl); }); it('throws error if user is unauthorized', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; @@ -39,9 +38,9 @@ describe('run trial session planning report', () => { ).rejects.toThrow(); }); - it('returns the created pdf', async () => { + it('returns the created pdf url', async () => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsClerk', }; @@ -63,22 +62,6 @@ describe('run trial session planning report', () => { }, ]); - applicationContext - .getTemplateGenerators() - .generateTrialSessionPlanningReportTemplate.mockImplementation( - async ({ - content: { previousTerms, rows, selectedTerm, selectedYear }, - }) => { - return `${previousTerms} ${rows} ${selectedTerm} ${selectedYear}`; - }, - ); - - applicationContext - .getUseCases() - .generatePdfFromHtmlInteractor.mockImplementation(({ contentHtml }) => { - return contentHtml; - }); - const result = await runTrialSessionPlanningReportInteractor({ applicationContext, term: 'winter', @@ -87,18 +70,10 @@ describe('run trial session planning report', () => { expect(result).toBe(mockPdfUrl); expect( - applicationContext.getTemplateGenerators() - .generateTrialSessionPlanningReportTemplate, - ).toBeCalled(); - expect( - applicationContext.getUseCases().generatePdfFromHtmlInteractor, - ).toBeCalled(); - expect(applicationContext.getUniqueId).toBeCalled(); - expect( - applicationContext.getPersistenceGateway().saveDocumentFromLambda, + applicationContext.getDocumentGenerators().trialSessionPlanningReport, ).toBeCalled(); expect( - applicationContext.getPersistenceGateway().getDownloadPolicyUrl, + applicationContext.getUseCaseHelpers().saveFileAndGenerateUrl, ).toBeCalled(); }); @@ -152,7 +127,7 @@ describe('run trial session planning report', () => { }, ]; user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsClerk', }; @@ -178,9 +153,7 @@ describe('run trial session planning report', () => { { term: 'spring', year: '2019' }, { term: 'winter', year: '2019' }, ]); - expect(results.trialLocationData.length).toEqual( - TrialSession.TRIAL_CITIES.ALL.length, - ); + expect(results.trialLocationData.length).toEqual(TRIAL_CITIES.ALL.length); expect(results.trialLocationData[0]).toMatchObject({ allCaseCount: 4, previousTermsData: [['(S) Ashford'], ['(S) Buch', '(R) Armen'], []], diff --git a/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.js b/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.js index 384febfd48f..a0f66bad2ff 100644 --- a/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.js +++ b/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.js @@ -5,9 +5,13 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); +const { + NOTICE_OF_TRIAL, + STANDING_PRETRIAL_NOTICE, + STANDING_PRETRIAL_ORDER, +} = require('../../entities/EntityConstants'); const { Case } = require('../../entities/cases/Case'); const { Document } = require('../../entities/Document'); -const { PDFDocument } = require('pdf-lib'); const { TrialSession } = require('../../entities/trialSessions/TrialSession'); const { UnauthorizedError } = require('../../../errors/errors'); @@ -28,6 +32,8 @@ exports.setNoticesForCalendaredTrialSessionInteractor = async ({ let shouldSetNoticesIssued = true; const user = applicationContext.getCurrentUser(); + const { PDFDocument } = await applicationContext.getPdfLib(); + if (!isAuthorized(user, ROLE_PERMISSIONS.TRIAL_SESSIONS)) { throw new UnauthorizedError('Unauthorized'); } @@ -116,8 +122,8 @@ exports.setNoticesForCalendaredTrialSessionInteractor = async ({ caseId: caseEntity.caseId, documentId: newNoticeOfTrialIssuedDocumentId, documentTitle: noticeOfTrialDocumentTitle, - documentType: Document.NOTICE_OF_TRIAL.documentType, - eventCode: Document.NOTICE_OF_TRIAL.eventCode, + documentType: NOTICE_OF_TRIAL.documentType, + eventCode: NOTICE_OF_TRIAL.eventCode, processingStatus: 'complete', userId: user.userId, }, @@ -142,10 +148,8 @@ exports.setNoticesForCalendaredTrialSessionInteractor = async ({ trialSessionId: trialSessionEntity.trialSessionId, }); - standingPretrialDocumentTitle = - Document.STANDING_PRETRIAL_NOTICE.documentType; - standingPretrialDocumentEventCode = - Document.STANDING_PRETRIAL_NOTICE.eventCode; + standingPretrialDocumentTitle = STANDING_PRETRIAL_NOTICE.documentType; + standingPretrialDocumentEventCode = STANDING_PRETRIAL_NOTICE.eventCode; } else { // Generate Standing Pretrial Order standingPretrialFile = await applicationContext @@ -156,10 +160,8 @@ exports.setNoticesForCalendaredTrialSessionInteractor = async ({ trialSessionId: trialSessionEntity.trialSessionId, }); - standingPretrialDocumentTitle = - Document.STANDING_PRETRIAL_ORDER.documentType; - standingPretrialDocumentEventCode = - Document.STANDING_PRETRIAL_ORDER.eventCode; + standingPretrialDocumentTitle = STANDING_PRETRIAL_ORDER.documentType; + standingPretrialDocumentEventCode = STANDING_PRETRIAL_ORDER.eventCode; } const newStandingPretrialDocumentId = applicationContext.getUniqueId(); diff --git a/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.test.js index dfc30a672a2..0f11bd0a3a5 100644 --- a/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/setNoticesForCalendaredTrialSessionInteractor.test.js @@ -1,39 +1,35 @@ -const fs = require('fs'); -const path = require('path'); const { applicationContext, } = require('../../test/createTestApplicationContext'); +const { + NOTICE_OF_TRIAL, + STANDING_PRETRIAL_NOTICE, + STANDING_PRETRIAL_ORDER, +} = require('../../entities/EntityConstants'); const { setNoticesForCalendaredTrialSessionInteractor, } = require('./setNoticesForCalendaredTrialSessionInteractor'); -const { Document } = require('../../entities/Document'); const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); const findNoticeOfTrial = caseRecord => { return caseRecord.documents.find( - document => document.documentType === Document.NOTICE_OF_TRIAL.documentType, + document => document.documentType === NOTICE_OF_TRIAL.documentType, ); }; const findStandingPretrialDocument = caseRecord => { return caseRecord.documents.find( document => - document.documentType === - Document.STANDING_PRETRIAL_NOTICE.documentType || - document.documentType === Document.STANDING_PRETRIAL_ORDER.documentType, + document.documentType === STANDING_PRETRIAL_NOTICE.documentType || + document.documentType === STANDING_PRETRIAL_ORDER.documentType, ); }; const fakeData = 'JVBERi0xLjEKJcKlwrHDqwoKMSAwIG9iagogIDw8IC9UeXBlIC9DYXRhbG9nCiAgICAgL1BhZ2VzIDIgMCBSCiAgPj4KZW5kb2JqCgoyIDAgb2JqCiAgPDwgL1R5cGUgL1BhZ2VzCiAgICAgL0tpZHMgWzMgMCBSXQogICAgIC9Db3VudCAxCiAgICAgL01lZGlhQm94IFswIDAgMzAwIDE0NF0KICA+PgplbmRvYmoKCjMgMCBvYmoKICA8PCAgL1R5cGUgL1BhZ2UKICAgICAgL1BhcmVudCAyIDAgUgogICAgICAvUmVzb3VyY2VzCiAgICAgICA8PCAvRm9udAogICAgICAgICAgIDw8IC9GMQogICAgICAgICAgICAgICA8PCAvVHlwZSAvRm9udAogICAgICAgICAgICAgICAgICAvU3VidHlwZSAvVHlwZTEKICAgICAgICAgICAgICAgICAgL0Jhc2VGb250IC9UaW1lcy1Sb21hbgogICAgICAgICAgICAgICA+PgogICAgICAgICAgID4+CiAgICAgICA+PgogICAgICAvQ29udGVudHMgNCAwIFIKICA+PgplbmRvYmoKCjQgMCBvYmoKICA8PCAvTGVuZ3RoIDg0ID4+CnN0cmVhbQogIEJUCiAgICAvRjEgMTggVGYKICAgIDUgODAgVGQKICAgIChDb25ncmF0aW9ucywgeW91IGZvdW5kIHRoZSBFYXN0ZXIgRWdnLikgVGoKICBFVAplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTggMDAwMDAgbiAKMDAwMDAwMDA3NyAwMDAwMCBuIAowMDAwMDAwMTc4IDAwMDAwIG4gCjAwMDAwMDA0NTcgMDAwMDAgbiAKdHJhaWxlcgogIDw8ICAvUm9vdCAxIDAgUgogICAgICAvU2l6ZSA1CiAgPj4Kc3RhcnR4cmVmCjU2NQolJUVPRgo='; -const testAssetsPath = path.join(__dirname, '../../../../test-assets/'); - -const testPdfDocBytes = () => { - return new Uint8Array(fs.readFileSync(testAssetsPath + 'sample.pdf')); -}; - const MOCK_TRIAL = { maxCases: 100, sessionType: 'Regular', @@ -43,8 +39,6 @@ const MOCK_TRIAL = { trialLocation: 'Birmingham, Alabama', }; -const testPdfDoc = testPdfDocBytes(); - let user; let calendaredCases; let trialSession; @@ -82,7 +76,7 @@ describe('setNoticesForCalendaredTrialSessionInteractor', () => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -129,10 +123,6 @@ describe('setNoticesForCalendaredTrialSessionInteractor', () => { }); }); - applicationContext - .getUseCaseHelpers() - .generatePaperServiceAddressPagePdf.mockResolvedValue(testPdfDoc); - applicationContext .getUseCases() .generateNoticeOfTrialIssuedInteractor.mockReturnValue(fakeData); @@ -147,7 +137,7 @@ describe('setNoticesForCalendaredTrialSessionInteractor', () => { it('Should return an unauthorized error if the user does not have the TRIAL_SESSIONS permission', async () => { user = new User({ name: 'Petitioner', - role: User.ROLES.petitioner, // Petitioners do not have the TRIAL_SESSIONS role, per authorizationClientService.js + role: ROLES.petitioner, // Petitioners do not have the TRIAL_SESSIONS role, per authorizationClientService.js userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -219,7 +209,7 @@ describe('setNoticesForCalendaredTrialSessionInteractor', () => { const findNoticeOfTrialDocketEntry = caseRecord => { return caseRecord.docketRecord.find( - entry => entry.description === Document.NOTICE_OF_TRIAL.documentType, + entry => entry.description === NOTICE_OF_TRIAL.documentType, ); }; @@ -359,7 +349,7 @@ describe('setNoticesForCalendaredTrialSessionInteractor', () => { const findNoticeOfTrialDocketEntry = caseRecord => { return caseRecord.docketRecord.find( - entry => entry.description === Document.NOTICE_OF_TRIAL.documentType, + entry => entry.description === NOTICE_OF_TRIAL.documentType, ); }; diff --git a/shared/src/business/useCases/trialSessions/setTrialSessionAsSwingSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/setTrialSessionAsSwingSessionInteractor.test.js index 196e77392af..b92a2a7630e 100644 --- a/shared/src/business/useCases/trialSessions/setTrialSessionAsSwingSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/setTrialSessionAsSwingSessionInteractor.test.js @@ -4,7 +4,7 @@ const { const { setTrialSessionAsSwingSessionInteractor, } = require('./setTrialSessionAsSwingSessionInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const MOCK_TRIAL_SESSION = { maxCases: 100, @@ -42,7 +42,7 @@ describe('Set trial session as swing session', () => { it('throws error if user is unauthorized', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; @@ -57,7 +57,7 @@ describe('Set trial session as swing session', () => { it('calls getTrialSessionById and updateTrialSession persistence methods with correct parameters', async () => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; diff --git a/shared/src/business/useCases/trialSessions/setTrialSessionCalendarInteractor.test.js b/shared/src/business/useCases/trialSessions/setTrialSessionCalendarInteractor.test.js index de09b155342..1ff06121c11 100644 --- a/shared/src/business/useCases/trialSessions/setTrialSessionCalendarInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/setTrialSessionCalendarInteractor.test.js @@ -4,9 +4,9 @@ const { const { setTrialSessionCalendarInteractor, } = require('./setTrialSessionCalendarInteractor'); -const { User } = require('../../entities/User'); - const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); +const { User } = require('../../entities/User'); const MOCK_TRIAL = { maxCases: 100, @@ -31,7 +31,7 @@ describe('setTrialSessionCalendarInteractor', () => { it('throws an exception when there is a permissions issue', async () => { user = new User({ name: 'Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); applicationContext @@ -63,7 +63,7 @@ describe('setTrialSessionCalendarInteractor', () => { user = new User({ name: 'petitionsClerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -109,7 +109,7 @@ describe('setTrialSessionCalendarInteractor', () => { user = new User({ name: 'petitionsClerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -149,7 +149,7 @@ describe('setTrialSessionCalendarInteractor', () => { it('should set work items as high priority for each case that is calendared', async () => { user = new User({ name: 'petitionsClerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); applicationContext diff --git a/shared/src/business/useCases/trialSessions/updateTrialSessionInteractor.test.js b/shared/src/business/useCases/trialSessions/updateTrialSessionInteractor.test.js index 5a155661b57..04d06fc7866 100644 --- a/shared/src/business/useCases/trialSessions/updateTrialSessionInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/updateTrialSessionInteractor.test.js @@ -6,6 +6,7 @@ const { } = require('./updateTrialSessionInteractor'); const { Case } = require('../../entities/cases/Case'); const { MOCK_CASE } = require('../../../test/mockCase'); +const { ROLES } = require('../../entities/EntityConstants'); const { User } = require('../../entities/User'); const MOCK_TRIAL = { @@ -66,7 +67,7 @@ describe('updateTrialSessionInteractor', () => { user = new User({ name: 'Docket Clerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -85,7 +86,7 @@ describe('updateTrialSessionInteractor', () => { it('throws error if user is unauthorized', async () => { user = new User({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); diff --git a/shared/src/business/useCases/trialSessions/updateTrialSessionWorkingCopyInteractor.test.js b/shared/src/business/useCases/trialSessions/updateTrialSessionWorkingCopyInteractor.test.js index 6baa46516bb..c8d856642e0 100644 --- a/shared/src/business/useCases/trialSessions/updateTrialSessionWorkingCopyInteractor.test.js +++ b/shared/src/business/useCases/trialSessions/updateTrialSessionWorkingCopyInteractor.test.js @@ -5,8 +5,8 @@ const { updateTrialSessionWorkingCopyInteractor, } = require('./updateTrialSessionWorkingCopyInteractor'); const { omit } = require('lodash'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); let user; @@ -49,7 +49,7 @@ describe('Update trial session working copy', () => { it('throws an error if the entity returned from persistence is invalid', async () => { user = { - role: User.ROLES.judge, + role: ROLES.judge, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }; @@ -69,7 +69,7 @@ describe('Update trial session working copy', () => { it('correctly returns data from persistence', async () => { user = { - role: User.ROLES.judge, + role: ROLES.judge, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }; diff --git a/shared/src/business/useCases/unblockCaseFromTrialInteractor.test.js b/shared/src/business/useCases/unblockCaseFromTrialInteractor.test.js index 45e907f40ff..2c5804c74c2 100644 --- a/shared/src/business/useCases/unblockCaseFromTrialInteractor.test.js +++ b/shared/src/business/useCases/unblockCaseFromTrialInteractor.test.js @@ -3,13 +3,13 @@ const { } = require('./unblockCaseFromTrialInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('unblockCaseFromTrialInteractor', () => { it('should set the blocked flag to false and remove the blockedReason', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: '7ad8dcbc-5978-4a29-8c41-02422b66f410', }); applicationContext .getPersistenceGateway() diff --git a/shared/src/business/useCases/unprioritizeCaseInteractor.test.js b/shared/src/business/useCases/unprioritizeCaseInteractor.test.js index 49eb071fc43..bc332f897e6 100644 --- a/shared/src/business/useCases/unprioritizeCaseInteractor.test.js +++ b/shared/src/business/useCases/unprioritizeCaseInteractor.test.js @@ -1,14 +1,14 @@ const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { MOCK_CASE } = require('../../test/mockCase'); +const { ROLES } = require('../entities/EntityConstants'); const { unprioritizeCaseInteractor } = require('./unprioritizeCaseInteractor'); -const { User } = require('../entities/User'); describe('unprioritizeCaseInteractor', () => { beforeAll(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: '7ad8dcbc-5978-4a29-8c41-02422b66f410', }); }); @@ -18,7 +18,7 @@ describe('unprioritizeCaseInteractor', () => { ...MOCK_CASE, highPriority: true, highPriorityReason: 'because', - status: Case.STATUS_TYPES.generalDocketReadyForTrial, + status: CASE_STATUS_TYPES.generalDocketReadyForTrial, }), ); @@ -51,7 +51,7 @@ describe('unprioritizeCaseInteractor', () => { ...MOCK_CASE, highPriority: true, highPriorityReason: 'because', - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }), ); diff --git a/shared/src/business/useCases/updateCaseContextInteractor.js b/shared/src/business/useCases/updateCaseContextInteractor.js index d432c44c432..7adb65e108d 100644 --- a/shared/src/business/useCases/updateCaseContextInteractor.js +++ b/shared/src/business/useCases/updateCaseContextInteractor.js @@ -3,6 +3,7 @@ const { ROLE_PERMISSIONS, } = require('../../authorization/authorizationClientService'); const { Case } = require('../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { TrialSession } = require('../entities/trialSessions/TrialSession'); const { UnauthorizedError } = require('../../errors/errors'); @@ -49,7 +50,7 @@ exports.updateCaseContextInteractor = async ({ // if this case status is changing FROM calendared // we need to remove it from the trial session if (caseStatus !== oldCase.status) { - if (oldCase.status === Case.STATUS_TYPES.calendared) { + if (oldCase.status === CASE_STATUS_TYPES.calendared) { const disposition = `Status was changed to ${caseStatus}`; const trialSession = await applicationContext @@ -72,7 +73,7 @@ exports.updateCaseContextInteractor = async ({ newCase.removeFromTrialWithAssociatedJudge(associatedJudge); } else if ( - oldCase.status === Case.STATUS_TYPES.generalDocketReadyForTrial + oldCase.status === CASE_STATUS_TYPES.generalDocketReadyForTrial ) { await applicationContext .getPersistenceGateway() @@ -82,7 +83,7 @@ exports.updateCaseContextInteractor = async ({ }); } - if (caseStatus === Case.STATUS_TYPES.generalDocketReadyForTrial) { + if (caseStatus === CASE_STATUS_TYPES.generalDocketReadyForTrial) { await applicationContext .getPersistenceGateway() .createCaseTrialSortMappingRecords({ diff --git a/shared/src/business/useCases/updateCaseContextInteractor.test.js b/shared/src/business/useCases/updateCaseContextInteractor.test.js index 8459b91579f..be447c410d8 100644 --- a/shared/src/business/useCases/updateCaseContextInteractor.test.js +++ b/shared/src/business/useCases/updateCaseContextInteractor.test.js @@ -1,16 +1,19 @@ +const { + CASE_STATUS_TYPES, + CHIEF_JUDGE, +} = require('../entities/EntityConstants'); const { updateCaseContextInteractor, } = require('./updateCaseContextInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('updateCaseContextInteractor', () => { beforeAll(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, - userId: 'docketClerk', + role: ROLES.docketClerk, + userId: '7ad8dcbc-5978-4a29-8c41-02422b66f410', }); }); @@ -27,15 +30,15 @@ describe('updateCaseContextInteractor', () => { updateCaseContextInteractor({ applicationContext, caseId: MOCK_CASE.caseId, - caseStatus: Case.STATUS_TYPES.cav, + caseStatus: CASE_STATUS_TYPES.cav, }), ).rejects.toThrow('Unauthorized for update case'); }); it('should call updateCase with the updated case status and return the updated case', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, - userId: 'docketClerk', + role: ROLES.docketClerk, + userId: '7ad8dcbc-5978-4a29-8c41-02422b66f410', }); applicationContext .getPersistenceGateway() @@ -44,9 +47,9 @@ describe('updateCaseContextInteractor', () => { const result = await updateCaseContextInteractor({ applicationContext, caseId: MOCK_CASE.caseId, - caseStatus: Case.STATUS_TYPES.cav, + caseStatus: CASE_STATUS_TYPES.cav, }); - expect(result.status).toEqual(Case.STATUS_TYPES.cav); + expect(result.status).toEqual(CASE_STATUS_TYPES.cav); }); it('should call updateCase and remove the case from trial if the old case status was calendared and the new case status is CAV', async () => { @@ -54,10 +57,10 @@ describe('updateCaseContextInteractor', () => { applicationContext, associatedJudge: 'Judge Rachael', caseId: MOCK_CASE.caseId, - caseStatus: Case.STATUS_TYPES.cav, + caseStatus: CASE_STATUS_TYPES.cav, }); - expect(result.status).toEqual(Case.STATUS_TYPES.cav); + expect(result.status).toEqual(CASE_STATUS_TYPES.cav); expect(result.associatedJudge).toEqual('Judge Rachael'); expect(result.trialSessionId).toBeUndefined(); }); @@ -66,27 +69,27 @@ describe('updateCaseContextInteractor', () => { const result = await updateCaseContextInteractor({ applicationContext, caseId: MOCK_CASE.caseId, - caseStatus: Case.STATUS_TYPES.generalDocket, + caseStatus: CASE_STATUS_TYPES.generalDocket, }); - expect(result.status).toEqual(Case.STATUS_TYPES.generalDocket); - expect(result.associatedJudge).toEqual(Case.CHIEF_JUDGE); + expect(result.status).toEqual(CASE_STATUS_TYPES.generalDocket); + expect(result.associatedJudge).toEqual(CHIEF_JUDGE); expect(result.trialSessionId).toBeUndefined(); }); it('should call updateCase and deleteCaseTrialSortMappingRecords if the old case status was Ready for Trial and the new status is different', async () => { applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ ...MOCK_CASE, - status: Case.STATUS_TYPES.generalDocketReadyForTrial, + status: CASE_STATUS_TYPES.generalDocketReadyForTrial, }); const result = await updateCaseContextInteractor({ applicationContext, caseId: MOCK_CASE.caseId, - caseStatus: Case.STATUS_TYPES.generalDocket, + caseStatus: CASE_STATUS_TYPES.generalDocket, }); - expect(result.status).toEqual(Case.STATUS_TYPES.generalDocket); + expect(result.status).toEqual(CASE_STATUS_TYPES.generalDocket); expect( applicationContext.getPersistenceGateway() .deleteCaseTrialSortMappingRecords, @@ -97,10 +100,10 @@ describe('updateCaseContextInteractor', () => { const result = await updateCaseContextInteractor({ applicationContext, caseId: MOCK_CASE.caseId, - caseStatus: Case.STATUS_TYPES.generalDocketReadyForTrial, + caseStatus: CASE_STATUS_TYPES.generalDocketReadyForTrial, }); - expect(result.status).toEqual(Case.STATUS_TYPES.generalDocketReadyForTrial); + expect(result.status).toEqual(CASE_STATUS_TYPES.generalDocketReadyForTrial); expect( applicationContext.getPersistenceGateway() .createCaseTrialSortMappingRecords, @@ -109,13 +112,13 @@ describe('updateCaseContextInteractor', () => { it('should only update the associated judge without changing the status if only the associated judge is passed in', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, - userId: 'docketClerk', + role: ROLES.docketClerk, + userId: '7ad8dcbc-5978-4a29-8c41-02422b66f410', }); applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ ...MOCK_CASE, associatedJudge: 'Judge Buch', - status: Case.STATUS_TYPES.submitted, + status: CASE_STATUS_TYPES.submitted, }); const result = await updateCaseContextInteractor({ @@ -123,7 +126,7 @@ describe('updateCaseContextInteractor', () => { associatedJudge: 'Judge Carluzzo', caseId: MOCK_CASE.caseId, }); - expect(result.status).toEqual(Case.STATUS_TYPES.submitted); + expect(result.status).toEqual(CASE_STATUS_TYPES.submitted); expect(result.associatedJudge).toEqual('Judge Carluzzo'); }); @@ -132,10 +135,10 @@ describe('updateCaseContextInteractor', () => { applicationContext, associatedJudge: 'Judge Carluzzo', caseId: MOCK_CASE.caseId, - caseStatus: Case.STATUS_TYPES.submitted, + caseStatus: CASE_STATUS_TYPES.submitted, }); - expect(result.status).toEqual(Case.STATUS_TYPES.submitted); + expect(result.status).toEqual(CASE_STATUS_TYPES.submitted); expect(result.associatedJudge).toEqual('Judge Carluzzo'); }); diff --git a/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.js b/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.js index 8c612a6cca7..f34975e5f9b 100644 --- a/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.js +++ b/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.js @@ -3,6 +3,7 @@ const { ROLE_PERMISSIONS, } = require('../../authorization/authorizationClientService'); const { Case } = require('../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { NotFoundError, UnauthorizedError } = require('../../errors/errors'); /** @@ -35,7 +36,7 @@ exports.updateCaseTrialSortTagsInteractor = async ({ throw new UnauthorizedError('Unauthorized for update case'); } - if (caseEntity.status === Case.STATUS_TYPES.generalDocketReadyForTrial) { + if (caseEntity.status === CASE_STATUS_TYPES.generalDocketReadyForTrial) { const caseSortTags = caseEntity.generateTrialSortTags(); await applicationContext diff --git a/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.test.js b/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.test.js index 868ccc482d1..ef7a9a8d33a 100644 --- a/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.test.js +++ b/shared/src/business/useCases/updateCaseTrialSortTagsInteractor.test.js @@ -3,8 +3,10 @@ const { } = require('./updateCaseTrialSortTagsInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { Case } = require('../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../entities/EntityConstants'); const { MOCK_CASE } = require('../../test/mockCase'); const { omit } = require('lodash'); +const { ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('Update case trial sort tags', () => { @@ -16,7 +18,7 @@ describe('Update case trial sort tags', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'bob', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); @@ -39,7 +41,7 @@ describe('Update case trial sort tags', () => { }); it('calls persistence if case status is ready for trial', async () => { - mockCase.status = Case.STATUS_TYPES.generalDocketReadyForTrial; + mockCase.status = CASE_STATUS_TYPES.generalDocketReadyForTrial; await updateCaseTrialSortTagsInteractor({ applicationContext, @@ -79,7 +81,7 @@ describe('Update case trial sort tags', () => { }); it('throws an error if the entity returned from persistence is invalid', async () => { - mockCase.status = Case.STATUS_TYPES.generalDocketReadyForTrial; + mockCase.status = CASE_STATUS_TYPES.generalDocketReadyForTrial; applicationContext .getPersistenceGateway() .getCaseByCaseId.mockReturnValue(omit(mockCase, 'docketNumber')); diff --git a/shared/src/business/useCases/updatePetitionDetailsInteractor.js b/shared/src/business/useCases/updatePetitionDetailsInteractor.js index ea451cca13a..02d23a96933 100644 --- a/shared/src/business/useCases/updatePetitionDetailsInteractor.js +++ b/shared/src/business/useCases/updatePetitionDetailsInteractor.js @@ -4,6 +4,7 @@ const { } = require('../../authorization/authorizationClientService'); const { Case } = require('../entities/cases/Case'); const { DocketRecord } = require('../entities/DocketRecord'); +const { PAYMENT_STATUS } = require('../entities/EntityConstants'); const { UnauthorizedError } = require('../../errors/errors'); /** @@ -41,10 +42,9 @@ exports.updatePetitionDetailsInteractor = async ({ .getPersistenceGateway() .getCaseByCaseId({ applicationContext, caseId }); - const isPaid = - editableFields.petitionPaymentStatus === Case.PAYMENT_STATUS.PAID; + const isPaid = editableFields.petitionPaymentStatus === PAYMENT_STATUS.PAID; const isWaived = - editableFields.petitionPaymentStatus === Case.PAYMENT_STATUS.WAIVED; + editableFields.petitionPaymentStatus === PAYMENT_STATUS.WAIVED; const newCase = new Case( { @@ -61,7 +61,7 @@ exports.updatePetitionDetailsInteractor = async ({ { applicationContext }, ); - if (oldCase.petitionPaymentStatus === Case.PAYMENT_STATUS.UNPAID) { + if (oldCase.petitionPaymentStatus === PAYMENT_STATUS.UNPAID) { if (isPaid) { newCase.addDocketRecord( new DocketRecord( diff --git a/shared/src/business/useCases/updatePetitionDetailsInteractor.test.js b/shared/src/business/useCases/updatePetitionDetailsInteractor.test.js index 1ba5cbf5f00..c3ea2acbf51 100644 --- a/shared/src/business/useCases/updatePetitionDetailsInteractor.test.js +++ b/shared/src/business/useCases/updatePetitionDetailsInteractor.test.js @@ -2,11 +2,11 @@ const { updatePetitionDetailsInteractor, } = require('./updatePetitionDetailsInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); const { cloneDeep } = require('lodash'); const { MOCK_CASE } = require('../../test/mockCase'); +const { PAYMENT_STATUS } = require('../entities/EntityConstants'); +const { ROLES } = require('../entities/EntityConstants'); const { UnauthorizedError } = require('../../errors/errors'); -const { User } = require('../entities/User'); describe('updatePetitionDetailsInteractor', () => { let mockCase; @@ -19,7 +19,7 @@ describe('updatePetitionDetailsInteractor', () => { mockCase = cloneDeep(MOCK_CASE); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketClerk', }); @@ -57,7 +57,7 @@ describe('updatePetitionDetailsInteractor', () => { caseId: mockCase.caseId, petitionDetails: { ...mockCase, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, }, }); @@ -67,7 +67,7 @@ describe('updatePetitionDetailsInteractor', () => { expect(result.petitionPaymentWaivedDate).toBe(null); expect(result.petitionPaymentMethod).toBe(null); expect(result.petitionPaymentDate).toBe(null); - expect(result.petitionPaymentStatus).toEqual(Case.PAYMENT_STATUS.UNPAID); + expect(result.petitionPaymentStatus).toEqual(PAYMENT_STATUS.UNPAID); }); it('should call updateCase with the updated case payment information (when paid) and return the updated case', async () => { @@ -78,7 +78,7 @@ describe('updatePetitionDetailsInteractor', () => { ...mockCase, petitionPaymentDate: '2019-11-30T09:10:11.000Z', petitionPaymentMethod: 'check', - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, + petitionPaymentStatus: PAYMENT_STATUS.PAID, }, }); @@ -88,7 +88,7 @@ describe('updatePetitionDetailsInteractor', () => { expect(result.petitionPaymentWaivedDate).toBe(null); expect(result.petitionPaymentDate).toEqual('2019-11-30T09:10:11.000Z'); expect(result.petitionPaymentMethod).toEqual('check'); - expect(result.petitionPaymentStatus).toEqual(Case.PAYMENT_STATUS.PAID); + expect(result.petitionPaymentStatus).toEqual(PAYMENT_STATUS.PAID); }); it('should call updateCase with the updated case payment information (when waived) and return the updated case', async () => { @@ -97,7 +97,7 @@ describe('updatePetitionDetailsInteractor', () => { caseId: mockCase.caseId, petitionDetails: { ...mockCase, - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, petitionPaymentWaivedDate: '2019-11-30T09:10:11.000Z', }, }); @@ -107,7 +107,7 @@ describe('updatePetitionDetailsInteractor', () => { ).toHaveBeenCalled(); expect(result.petitionPaymentDate).toBe(null); expect(result.petitionPaymentMethod).toBe(null); - expect(result.petitionPaymentStatus).toEqual(Case.PAYMENT_STATUS.WAIVED); + expect(result.petitionPaymentStatus).toEqual(PAYMENT_STATUS.WAIVED); expect(result.petitionPaymentWaivedDate).toEqual( '2019-11-30T09:10:11.000Z', ); @@ -116,7 +116,7 @@ describe('updatePetitionDetailsInteractor', () => { it('should create a docket entry when moved from unpaid to waived', async () => { applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ ...mockCase, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, }); const result = await updatePetitionDetailsInteractor({ @@ -124,7 +124,7 @@ describe('updatePetitionDetailsInteractor', () => { caseId: mockCase.caseId, petitionDetails: { ...mockCase, - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, petitionPaymentWaivedDate: '2019-11-30T09:10:11.000Z', }, }); @@ -147,7 +147,7 @@ describe('updatePetitionDetailsInteractor', () => { it('should create a docket entry when moved from unpaid to paid', async () => { applicationContext.getPersistenceGateway().getCaseByCaseId.mockReturnValue({ ...MOCK_CASE, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, }); const result = await updatePetitionDetailsInteractor({ @@ -157,7 +157,7 @@ describe('updatePetitionDetailsInteractor', () => { ...mockCase, petitionPaymentDate: '2019-11-30T09:10:11.000Z', petitionPaymentMethod: 'check', - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, + petitionPaymentStatus: PAYMENT_STATUS.PAID, }, }); diff --git a/shared/src/business/useCases/updatePetitionerInformationInteractor.js b/shared/src/business/useCases/updatePetitionerInformationInteractor.js index c2b7c0893ac..2a465301ac4 100644 --- a/shared/src/business/useCases/updatePetitionerInformationInteractor.js +++ b/shared/src/business/useCases/updatePetitionerInformationInteractor.js @@ -13,7 +13,6 @@ const { addCoverToPdf } = require('./addCoversheetInteractor'); const { Case } = require('../entities/cases/Case'); const { Document } = require('../entities/Document'); const { getCaseCaptionMeta } = require('../utilities/getCaseCaptionMeta'); -const { PDFDocument } = require('pdf-lib'); const { UnauthorizedError } = require('../../errors/errors'); /** @@ -34,6 +33,8 @@ exports.updatePetitionerInformationInteractor = async ({ contactSecondary, partyType, }) => { + const { PDFDocument } = await applicationContext.getPdfLib(); + const user = applicationContext.getCurrentUser(); if (!isAuthorized(user, ROLE_PERMISSIONS.EDIT_PETITION_DETAILS)) { @@ -167,7 +168,7 @@ exports.updatePetitionerInformationInteractor = async ({ headerHtml: null, }); - const changeOfAddressPdfWithCover = await addCoverToPdf({ + const { pdfData: changeOfAddressPdfWithCover } = await addCoverToPdf({ applicationContext, caseEntity, documentEntity: changeOfAddressDocument, @@ -224,6 +225,7 @@ exports.updatePetitionerInformationInteractor = async ({ if (primaryPdf) { await copyToNewPdf({ addressPages, + applicationContext, newPdfDoc: fullDocument, noticeDoc: await PDFDocument.load(primaryPdf), }); @@ -231,6 +233,7 @@ exports.updatePetitionerInformationInteractor = async ({ if (secondaryPdf) { await copyToNewPdf({ addressPages, + applicationContext, newPdfDoc: fullDocument, noticeDoc: await PDFDocument.load(secondaryPdf), }); diff --git a/shared/src/business/useCases/updatePetitionerInformationInteractor.test.js b/shared/src/business/useCases/updatePetitionerInformationInteractor.test.js index 558e8afa7ea..696c36f8ba1 100644 --- a/shared/src/business/useCases/updatePetitionerInformationInteractor.test.js +++ b/shared/src/business/useCases/updatePetitionerInformationInteractor.test.js @@ -1,5 +1,3 @@ -const fs = require('fs'); -const path = require('path'); const { createISODateString, formatDateString, @@ -11,35 +9,20 @@ const { updatePetitionerInformationInteractor, } = require('./updatePetitionerInformationInteractor'); const { Case } = require('../entities/cases/Case'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { MOCK_CASE } = require('../../test/mockCase'); -const { SERVICE_INDICATOR_TYPES } = require('../entities/cases/CaseConstants'); +const { PARTY_TYPES, ROLES } = require('../entities/EntityConstants'); +const { SERVICE_INDICATOR_TYPES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); +let { applicationContext } = require('../test/createTestApplicationContext'); const fakeData = 'JVBERi0xLjEKJcKlwrHDqwoKMSAwIG9iagogIDw8IC9UeXBlIC9DYXRhbG9nCiAgICAgL1BhZ2VzIDIgMCBSCiAgPj4KZW5kb2JqCgoyIDAgb2JqCiAgPDwgL1R5cGUgL1BhZ2VzCiAgICAgL0tpZHMgWzMgMCBSXQogICAgIC9Db3VudCAxCiAgICAgL01lZGlhQm94IFswIDAgMzAwIDE0NF0KICA+PgplbmRvYmoKCjMgMCBvYmoKICA8PCAgL1R5cGUgL1BhZ2UKICAgICAgL1BhcmVudCAyIDAgUgogICAgICAvUmVzb3VyY2VzCiAgICAgICA8PCAvRm9udAogICAgICAgICAgIDw8IC9GMQogICAgICAgICAgICAgICA8PCAvVHlwZSAvRm9udAogICAgICAgICAgICAgICAgICAvU3VidHlwZSAvVHlwZTEKICAgICAgICAgICAgICAgICAgL0Jhc2VGb250IC9UaW1lcy1Sb21hbgogICAgICAgICAgICAgICA+PgogICAgICAgICAgID4+CiAgICAgICA+PgogICAgICAvQ29udGVudHMgNCAwIFIKICA+PgplbmRvYmoKCjQgMCBvYmoKICA8PCAvTGVuZ3RoIDg0ID4+CnN0cmVhbQogIEJUCiAgICAvRjEgMTggVGYKICAgIDUgODAgVGQKICAgIChDb25ncmF0aW9ucywgeW91IGZvdW5kIHRoZSBFYXN0ZXIgRWdnLikgVGoKICBFVAplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTggMDAwMDAgbiAKMDAwMDAwMDA3NyAwMDAwMCBuIAowMDAwMDAwMTc4IDAwMDAwIG4gCjAwMDAwMDA0NTcgMDAwMDAgbiAKdHJhaWxlcgogIDw8ICAvUm9vdCAxIDAgUgogICAgICAvU2l6ZSA1CiAgPj4Kc3RhcnR4cmVmCjU2NQolJUVPRgo='; -const testAssetsPath = path.join(__dirname, '../../../test-assets/'); - -const testPdfDocBytes = () => { - // sample.pdf is a 1 page document - return new Uint8Array( - new Uint8Array(fs.readFileSync(testAssetsPath + 'sample.pdf')), - ); -}; - -let testPdfDoc; - -testPdfDoc = testPdfDocBytes(); - const updateCaseStub = jest.fn(); const generateChangeOfAddressTemplateStub = jest.fn(); const generatePdfFromHtmlInteractorStub = jest.fn(); const getAddressPhoneDiffStub = jest.fn(); const saveDocumentFromLambdaStub = jest.fn(); -const generatePaperServiceAddressPagePdfMock = jest - .fn() - .mockResolvedValue(testPdfDoc); const sendServedPartiesEmailsMock = jest.fn(); let persistenceGateway = { @@ -62,11 +45,13 @@ const useCases = { const userData = { name: 'administrator', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }; let userObj = userData; -const applicationContext = { + +applicationContext = { + ...applicationContext, environment: { stage: 'local' }, getCaseTitle: Case.getCaseTitle, getChromiumBrowser: () => ({ @@ -92,7 +77,6 @@ const applicationContext = { }, getUniqueId: () => 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', getUseCaseHelpers: () => ({ - generatePaperServiceAddressPagePdf: generatePaperServiceAddressPagePdfMock, sendServedPartiesEmails: sendServedPartiesEmailsMock, }), getUseCases: () => useCases, @@ -129,7 +113,7 @@ describe('update petitioner contact information on a case', () => { applicationContext, caseId: 'a805d1ab-18d0-43ec-bafb-654e83405416', contactPrimary: MOCK_CASE.contactPrimary, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); expect(generateChangeOfAddressTemplateStub).not.toHaveBeenCalled(); expect(generatePdfFromHtmlInteractorStub).not.toHaveBeenCalled(); @@ -143,7 +127,7 @@ describe('update petitioner contact information on a case', () => { caseId: 'a805d1ab-18d0-43ec-bafb-654e83405416', contactPrimary: MOCK_CASE.contactPrimary, contactSecondary: { countryType: 'domestic' }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }), ).rejects.toThrow(); expect(generateChangeOfAddressTemplateStub).not.toHaveBeenCalled(); @@ -166,7 +150,7 @@ describe('update petitioner contact information on a case', () => { state: 'TN', title: 'Executor', }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); expect(updateCaseStub).toHaveBeenCalled(); expect(generateChangeOfAddressTemplateStub).toHaveBeenCalled(); @@ -189,7 +173,7 @@ describe('update petitioner contact information on a case', () => { state: 'TN', title: 'Executor', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(updateCaseStub).toHaveBeenCalled(); expect(generateChangeOfAddressTemplateStub).not.toHaveBeenCalled(); @@ -210,7 +194,7 @@ describe('update petitioner contact information on a case', () => { state: 'TN', title: 'Executor', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); const result = await updatePetitionerInformationInteractor({ @@ -227,7 +211,7 @@ describe('update petitioner contact information on a case', () => { state: 'TN', title: 'Executor', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(updateCaseStub).toHaveBeenCalled(); expect(generateChangeOfAddressTemplateStub).toHaveBeenCalled(); @@ -244,7 +228,7 @@ describe('update petitioner contact information on a case', () => { ...MOCK_CASE.contactPrimary, serviceIndicator: SERVICE_INDICATOR_TYPES.SI_PAPER, }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); expect(updateCaseStub).toHaveBeenCalled(); expect(generateChangeOfAddressTemplateStub).not.toHaveBeenCalled(); @@ -261,7 +245,7 @@ describe('update petitioner contact information on a case', () => { ...MOCK_CASE.contactPrimary, email: 'test@example.com', }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); expect(updateCaseStub).toHaveBeenCalled(); expect( @@ -279,7 +263,7 @@ describe('update petitioner contact information on a case', () => { countryType: 'alien', serviceIndicator: SERVICE_INDICATOR_TYPES.SI_PAPER, }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }), ).rejects.toThrow('The Case entity was invalid'); expect(updateCaseStub).not.toHaveBeenCalled(); @@ -289,7 +273,7 @@ describe('update petitioner contact information on a case', () => { persistenceGateway.getCaseByCaseId = async () => ({ ...MOCK_CASE, }); - userObj.role = User.ROLES.petitioner; + userObj.role = ROLES.petitioner; await expect( updatePetitionerInformationInteractor({ applicationContext, diff --git a/shared/src/business/useCases/updatePrimaryContactInteractor.js b/shared/src/business/useCases/updatePrimaryContactInteractor.js index a89b7c09a97..ac29f0d54fe 100644 --- a/shared/src/business/useCases/updatePrimaryContactInteractor.js +++ b/shared/src/business/useCases/updatePrimaryContactInteractor.js @@ -4,7 +4,7 @@ const { const { addCoverToPdf } = require('./addCoversheetInteractor'); const { capitalize } = require('lodash'); const { Case } = require('../entities/cases/Case'); -const { DOCKET_SECTION } = require('../entities/WorkQueue'); +const { DOCKET_SECTION } = require('../entities/EntityConstants'); const { Document } = require('../entities/Document'); const { getCaseCaptionMeta } = require('../utilities/getCaseCaptionMeta'); const { Message } = require('../entities/Message'); @@ -141,7 +141,8 @@ exports.updatePrimaryContactInteractor = async ({ }, isQC: true, section: DOCKET_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, }, { applicationContext }, ); @@ -162,7 +163,7 @@ exports.updatePrimaryContactInteractor = async ({ caseEntity.addDocument(changeOfAddressDocument, { applicationContext }); - const changeOfAddressPdfWithCover = await addCoverToPdf({ + const { pdfData: changeOfAddressPdfWithCover } = await addCoverToPdf({ applicationContext, caseEntity, documentEntity: changeOfAddressDocument, diff --git a/shared/src/business/useCases/updatePrimaryContactInteractor.test.js b/shared/src/business/useCases/updatePrimaryContactInteractor.test.js index ce3e3b38cc6..50878993860 100644 --- a/shared/src/business/useCases/updatePrimaryContactInteractor.test.js +++ b/shared/src/business/useCases/updatePrimaryContactInteractor.test.js @@ -3,6 +3,7 @@ const { } = require('./updatePrimaryContactInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../test/mockCase'); +const { ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); const fakeData = @@ -27,7 +28,7 @@ describe('update primary contact on a case', () => { applicationContext.getCurrentUser.mockReturnValue( new User({ name: 'bob', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }), ); diff --git a/shared/src/business/useCases/updateQcCompleteForTrialInteractor.test.js b/shared/src/business/useCases/updateQcCompleteForTrialInteractor.test.js index f503af60043..262ed9de807 100644 --- a/shared/src/business/useCases/updateQcCompleteForTrialInteractor.test.js +++ b/shared/src/business/useCases/updateQcCompleteForTrialInteractor.test.js @@ -3,7 +3,7 @@ const { } = require('./updateQcCompleteForTrialInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../test/mockCase'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('updateQcCompleteForTrialInteractor', () => { let user; @@ -22,7 +22,7 @@ describe('updateQcCompleteForTrialInteractor', () => { it('should throw an error if the user is unauthorized to update a trial session', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; @@ -38,7 +38,7 @@ describe('updateQcCompleteForTrialInteractor', () => { it('should call updateCase with the updated qcCompleteForTrial value and return the updated case', async () => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsClerk', }; diff --git a/shared/src/business/useCases/updateSecondaryContactInteractor.js b/shared/src/business/useCases/updateSecondaryContactInteractor.js index e45cb53e9b8..404dbb798d7 100644 --- a/shared/src/business/useCases/updateSecondaryContactInteractor.js +++ b/shared/src/business/useCases/updateSecondaryContactInteractor.js @@ -4,7 +4,7 @@ const { const { addCoverToPdf } = require('./addCoversheetInteractor'); const { capitalize } = require('lodash'); const { Case } = require('../entities/cases/Case'); -const { DOCKET_SECTION } = require('../entities/WorkQueue'); +const { DOCKET_SECTION } = require('../entities/EntityConstants'); const { Document } = require('../entities/Document'); const { getCaseCaptionMeta } = require('../utilities/getCaseCaptionMeta'); const { Message } = require('../entities/Message'); @@ -150,7 +150,8 @@ exports.updateSecondaryContactInteractor = async ({ }, isQC: true, section: DOCKET_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, }, { applicationContext }, ); @@ -171,7 +172,7 @@ exports.updateSecondaryContactInteractor = async ({ caseEntity.addDocument(changeOfAddressDocument, { applicationContext }); - const docketRecordPdfWithCover = await addCoverToPdf({ + const { pdfData: docketRecordPdfWithCover } = await addCoverToPdf({ applicationContext, caseEntity, documentEntity: changeOfAddressDocument, diff --git a/shared/src/business/useCases/updateSecondaryContactInteractor.test.js b/shared/src/business/useCases/updateSecondaryContactInteractor.test.js index d07d144d808..37679559a15 100644 --- a/shared/src/business/useCases/updateSecondaryContactInteractor.test.js +++ b/shared/src/business/useCases/updateSecondaryContactInteractor.test.js @@ -3,6 +3,7 @@ const { } = require('./updateSecondaryContactInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { MOCK_CASE } = require('../../test/mockCase'); +const { ROLES } = require('../entities/EntityConstants'); const { User } = require('../entities/User'); describe('updateSecondaryContactInteractor', () => { @@ -26,7 +27,7 @@ describe('updateSecondaryContactInteractor', () => { let mockUser = new User({ name: 'bob', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '6805d1ab-18d0-43ec-bafb-654e83405416', }); diff --git a/shared/src/business/useCases/users/createUserInteractor.js b/shared/src/business/useCases/users/createUserInteractor.js index 4c891160eef..b1d92908c44 100644 --- a/shared/src/business/useCases/users/createUserInteractor.js +++ b/shared/src/business/useCases/users/createUserInteractor.js @@ -6,6 +6,7 @@ const { ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); const { Practitioner } = require('../../entities/Practitioner'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); const { User } = require('../../entities/User'); @@ -28,9 +29,9 @@ exports.createUserInteractor = async ({ applicationContext, user }) => { if ( [ - User.ROLES.privatePractitioner, - User.ROLES.irsPractitioner, - User.ROLES.inactivePractitioner, + ROLES.privatePractitioner, + ROLES.irsPractitioner, + ROLES.inactivePractitioner, ].includes(user.role) ) { userEntity = new Practitioner( diff --git a/shared/src/business/useCases/users/createUserInteractor.test.js b/shared/src/business/useCases/users/createUserInteractor.test.js index dbbc1b23531..99a29112e05 100644 --- a/shared/src/business/useCases/users/createUserInteractor.test.js +++ b/shared/src/business/useCases/users/createUserInteractor.test.js @@ -5,13 +5,13 @@ const { UnauthorizedError, } = require('../../../../../shared/src/errors/errors'); const { createUserInteractor } = require('./createUserInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('create user', () => { it('creates the user', async () => { const mockUser = { name: 'Test PetitionsClerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk1@example.com', }; applicationContext.getCurrentUser.mockReturnValue({ @@ -23,7 +23,7 @@ describe('create user', () => { .createUser.mockReturnValue(mockUser); const userToCreate = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk1@example.com', }; const user = await createUserInteractor({ @@ -36,11 +36,11 @@ describe('create user', () => { it('throws unauthorized for any user without an "admin" role', async () => { const mockUser = { name: 'Test Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner1@example.com', }; applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'admin', }); applicationContext @@ -64,7 +64,7 @@ describe('create user', () => { applicationContext.getPersistenceGateway().createUser.mockReturnValue({ barNumber: 'CS20001', name: 'Test PrivatePractitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '745b7d39-8fae-4c2f-893c-3c829598bc71', }); @@ -77,7 +77,7 @@ describe('create user', () => { lastName: 'PrivatePractitioner', originalBarState: 'CA', practitionerType: 'Attorney', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }; const user = await createUserInteractor({ @@ -87,7 +87,7 @@ describe('create user', () => { expect(user).toMatchObject({ barNumber: 'CS20001', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); }); @@ -99,7 +99,7 @@ describe('create user', () => { applicationContext.getPersistenceGateway().createUser.mockReturnValue({ barNumber: 'CS20001', name: 'Test PrivatePractitioner', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '745b7d39-8fae-4c2f-893c-3c829598bc71', }); const mockAdmissionsDate = new Date('1876/02/19').toISOString(); @@ -115,7 +115,7 @@ describe('create user', () => { lastName: 'IRSPractitioner', originalBarState: 'CA', practitionerType: 'Attorney', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }, }); @@ -133,7 +133,7 @@ describe('create user', () => { applicationContext.getPersistenceGateway().createUser.mockReturnValue({ barNumber: 'CS20001', name: 'Test InactivePractitioner', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, userId: '745b7d39-8fae-4c2f-893c-3c829598bc71', }); const mockAdmissionsDate = new Date('1876/02/19').toISOString(); @@ -149,13 +149,13 @@ describe('create user', () => { lastName: 'IRSPractitioner', originalBarState: 'CA', practitionerType: 'Attorney', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, }, }); expect(user).toMatchObject({ barNumber: 'CS20001', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, }); }); }); diff --git a/shared/src/business/useCases/users/generateChangeOfAddress.js b/shared/src/business/useCases/users/generateChangeOfAddress.js index 20c4cad7836..f761002e445 100644 --- a/shared/src/business/useCases/users/generateChangeOfAddress.js +++ b/shared/src/business/useCases/users/generateChangeOfAddress.js @@ -8,11 +8,12 @@ const { const { addCoverToPdf } = require('../addCoversheetInteractor'); const { capitalize, clone } = require('lodash'); const { Case } = require('../../entities/cases/Case'); -const { DOCKET_SECTION } = require('../../entities/WorkQueue'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); +const { DOCKET_SECTION } = require('../../entities/EntityConstants'); const { Document } = require('../../entities/Document'); const { getCaseCaptionMeta } = require('../../utilities/getCaseCaptionMeta'); const { Message } = require('../../entities/Message'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const { WorkItem } = require('../../entities/WorkItem'); exports.generateChangeOfAddress = async ({ @@ -63,7 +64,7 @@ exports.generateChangeOfAddress = async ({ }; let closedMoreThan6Months; - if (caseEntity.status === Case.STATUS_TYPES.closed) { + if (caseEntity.status === CASE_STATUS_TYPES.closed) { const maxClosedDate = calculateISODate({ dateString: caseEntity.closedDate, howMuch: 6, @@ -73,9 +74,9 @@ exports.generateChangeOfAddress = async ({ closedMoreThan6Months = maxClosedDate <= rightNow; } - const shouldGenerateNotice = caseEntity.status !== Case.STATUS_TYPES.closed; + const shouldGenerateNotice = caseEntity.status !== CASE_STATUS_TYPES.closed; const shouldUpdateCase = - !closedMoreThan6Months || caseEntity.status !== Case.STATUS_TYPES.closed; + !closedMoreThan6Months || caseEntity.status !== CASE_STATUS_TYPES.closed; if (shouldGenerateNotice) { const documentType = applicationContext @@ -129,14 +130,14 @@ exports.generateChangeOfAddress = async ({ userId: user.userId, }; - if (user.role === User.ROLES.privatePractitioner) { + if (user.role === ROLES.privatePractitioner) { documentData.privatePractitioners = [ { name, partyPrivatePractitioner: true, }, ]; - } else if (user.role === User.ROLES.irsPractitioner) { + } else if (user.role === ROLES.irsPractitioner) { documentData.partyIrsPractitioner = true; } @@ -172,7 +173,8 @@ exports.generateChangeOfAddress = async ({ }, isQC: true, section: DOCKET_SECTION, - sentBy: user.userId, + sentBy: user.name, + sentByUserId: user.userId, }, { applicationContext }, ); @@ -193,7 +195,7 @@ exports.generateChangeOfAddress = async ({ caseEntity.addDocument(changeOfAddressDocument, { applicationContext }); - const docketRecordPdfWithCover = await addCoverToPdf({ + const { pdfData: docketRecordPdfWithCover } = await addCoverToPdf({ applicationContext, caseEntity, documentEntity: changeOfAddressDocument, diff --git a/shared/src/business/useCases/users/generateChangeOfAddress.test.js b/shared/src/business/useCases/users/generateChangeOfAddress.test.js index 190363a0c52..d888a155bb6 100644 --- a/shared/src/business/useCases/users/generateChangeOfAddress.test.js +++ b/shared/src/business/useCases/users/generateChangeOfAddress.test.js @@ -26,7 +26,7 @@ describe('generateChangeOfAddress', () => { postalCode: '61234', state: 'IL', }, - email: 'privatePractitioner1', + email: 'privatePractitioner1@example.com', name: 'Test Private Practitioner', representingPrimary: true, role: 'privatePractitioner', diff --git a/shared/src/business/useCases/users/getInternalUsersInteractor.test.js b/shared/src/business/useCases/users/getInternalUsersInteractor.test.js index 6733124bc59..e8c250c89c7 100644 --- a/shared/src/business/useCases/users/getInternalUsersInteractor.test.js +++ b/shared/src/business/useCases/users/getInternalUsersInteractor.test.js @@ -2,25 +2,25 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); const { getInternalUsersInteractor } = require('./getInternalUsersInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('Get internal users', () => { beforeEach(() => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketclerk', }); applicationContext .getPersistenceGateway() .getInternalUsers.mockReturnValue([ { - userId: 'abc', + userId: '343db562-5187-49e3-97fe-90f5fa70b9d4', }, { - userId: '123', + userId: 'a34fd25c-a2d0-4f89-b495-c52805c9fdd0', }, { - userId: 'gg', + userId: 'bed3b49a-283c-491b-a1c5-0ece5832c6f4', }, ]); }); @@ -29,20 +29,20 @@ describe('Get internal users', () => { const users = await getInternalUsersInteractor({ applicationContext }); expect(users).toMatchObject([ { - userId: 'abc', + userId: '343db562-5187-49e3-97fe-90f5fa70b9d4', }, { - userId: '123', + userId: 'a34fd25c-a2d0-4f89-b495-c52805c9fdd0', }, { - userId: 'gg', + userId: 'bed3b49a-283c-491b-a1c5-0ece5832c6f4', }, ]); }); it('throws unauthorized error for unauthorized users', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); let error; diff --git a/shared/src/business/useCases/users/getIrsPractitionersBySearchKeyInteractor.test.js b/shared/src/business/useCases/users/getIrsPractitionersBySearchKeyInteractor.test.js index 36d41f89e24..bb0e6fedc99 100644 --- a/shared/src/business/useCases/users/getIrsPractitionersBySearchKeyInteractor.test.js +++ b/shared/src/business/useCases/users/getIrsPractitionersBySearchKeyInteractor.test.js @@ -4,7 +4,7 @@ const { const { getIrsPractitionersBySearchKeyInteractor, } = require('./getIrsPractitionersBySearchKeyInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); let user; @@ -16,8 +16,8 @@ describe('getIrsPractitionersBySearchKeyInteractor', () => { it('should throw an error when not authorized', async () => { user = { - name: 'Olivia Jade', - role: User.ROLES.petitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.petitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; @@ -39,8 +39,8 @@ describe('getIrsPractitionersBySearchKeyInteractor', () => { it('should return users from persistence', async () => { user = { - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.petitionsClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; diff --git a/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.js b/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.js index 4f248bc5203..284594558e0 100644 --- a/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.js +++ b/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.js @@ -1,4 +1,4 @@ -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); /** * getJudgeForUserChambersInteractor - returns the judge user for a given user in a chambers section @@ -14,9 +14,9 @@ exports.getJudgeForUserChambersInteractor = async ({ user, }) => { let judgeUser; - if (user.role === User.ROLES.judge) { + if (user.role === ROLES.judge) { judgeUser = user; - } else if (user.role === User.ROLES.chambers) { + } else if (user.role === ROLES.chambers) { let chambersSection; if (user.section) { chambersSection = user.section; @@ -34,7 +34,7 @@ exports.getJudgeForUserChambersInteractor = async ({ section: chambersSection, }); - judgeUser = sectionUsers.find(user => user.role === User.ROLES.judge); + judgeUser = sectionUsers.find(user => user.role === ROLES.judge); } return judgeUser; diff --git a/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.test.js b/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.test.js index 25a5f48dbf8..8a3609f6145 100644 --- a/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.test.js +++ b/shared/src/business/useCases/users/getJudgeForUserChambersInteractor.test.js @@ -2,42 +2,42 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); import { getJudgeForUserChambersInteractor } from './getJudgeForUserChambersInteractor'; -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); let currentUser; const chambersUser = { - role: User.ROLES.chambers, + role: ROLES.chambers, section: 'judgesChambers', userId: 'chambers1', }; const judgeUser = { - role: User.ROLES.judge, + role: ROLES.judge, section: 'judgesChambers', userId: 'judge1', }; const allUsers = [ { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, section: 'docket', userId: 'docketclerk1', }, { ...judgeUser }, { - role: User.ROLES.judge, + role: ROLES.judge, section: 'judgesChambers2', userId: 'judge2', }, { ...chambersUser }, { - role: User.ROLES.chambers, + role: ROLES.chambers, section: 'judgesChambers', userId: 'chambers2', }, { - role: User.ROLES.chambers, + role: ROLES.chambers, section: 'judgesChambers2', userId: 'chambers3', }, @@ -110,7 +110,7 @@ describe('getJudgeForUserChambersInteractor', () => { it('Returns no user if the given user is not associated with any chambers section', async () => { currentUser = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketclerk1', }; diff --git a/shared/src/business/useCases/users/getPrivatePractitionersBySearchKeyInteractor.test.js b/shared/src/business/useCases/users/getPrivatePractitionersBySearchKeyInteractor.test.js index 4e88c4a819e..e74b98a8eea 100644 --- a/shared/src/business/useCases/users/getPrivatePractitionersBySearchKeyInteractor.test.js +++ b/shared/src/business/useCases/users/getPrivatePractitionersBySearchKeyInteractor.test.js @@ -4,7 +4,7 @@ const { const { getPrivatePractitionersBySearchKeyInteractor, } = require('./getPrivatePractitionersBySearchKeyInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); let user; describe('getPrivatePractitionersBySearchKeyInteractor', () => { @@ -16,8 +16,8 @@ describe('getPrivatePractitionersBySearchKeyInteractor', () => { it('should throw an error when not authorized', async () => { let error; user = { - name: 'Olivia Jade', - role: User.ROLES.petitioner, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.petitioner, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; applicationContext @@ -37,8 +37,8 @@ describe('getPrivatePractitionersBySearchKeyInteractor', () => { it('should return users from persistence', async () => { user = { - name: 'Olivia Jade', - role: User.ROLES.petitionsClerk, + name: 'Emmett Lathrop "Doc" Brown, Ph.D.', + role: ROLES.petitionsClerk, userId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', }; applicationContext diff --git a/shared/src/business/useCases/users/getUsersInSectionInteractor.test.js b/shared/src/business/useCases/users/getUsersInSectionInteractor.test.js index cb552c313a4..eebf004dea8 100644 --- a/shared/src/business/useCases/users/getUsersInSectionInteractor.test.js +++ b/shared/src/business/useCases/users/getUsersInSectionInteractor.test.js @@ -5,39 +5,39 @@ const { getUsersInSectionInteractor, } = require('./getUsersInSectionInteractor'); const { NotFoundError, UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const MOCK_SECTION = [ { - name: 'Test Petitioner', - role: User.ROLES.petitioner, - userId: 'petitioner1@example.com', + name: 'Test Petitioner 1', + role: ROLES.petitioner, + userId: '304a756b-ce23-438a-a9bb-3732c6a439b7', }, { - name: 'Test Petitioner', - role: User.ROLES.petitioner, - userId: 'petitioner2@example.com', + name: 'Test Petitioner 2', + role: ROLES.petitioner, + userId: 'a79d2fac-aa2c-4183-9877-01ab1cdff127', }, ]; const MOCK_JUDGE_SECTION = [ { - name: 'Test Judge', - role: User.ROLES.judge, - userId: 'judge@example.com', + name: 'Test Judge 1', + role: ROLES.judge, + userId: 'ce5add74-1559-448d-a67d-c887c8351b2e', }, { - name: 'Test Judge2', - role: User.ROLES.judge, - userId: 'judge2@example.com', + name: 'Test Judge 2', + role: ROLES.judge, + userId: 'ea83cea2-5ce9-451d-b3d6-1e7c0e51d311', }, ]; describe('Get users in section', () => { it('retrieves the users in the petitions section', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: '5a797be2-a4b7-469f-9cfb-32b7f169d489', }); applicationContext .getPersistenceGateway() @@ -48,13 +48,13 @@ describe('Get users in section', () => { sectionToGet, }); expect(section.length).toEqual(2); - expect(section[0].userId).toEqual('petitioner1@example.com'); + expect(section[0].name).toEqual('Test Petitioner 1'); }); it('returns notfounderror when section not found', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, - userId: 'petitionsclerk', + role: ROLES.petitionsClerk, + userId: '5a797be2-a4b7-469f-9cfb-32b7f169d489', }); applicationContext .getPersistenceGateway() @@ -76,8 +76,8 @@ describe('Get users in section', () => { it('returns unauthorizederror when user not authorized', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '5a797be2-a4b7-469f-9cfb-32b7f169d489', }); applicationContext .getPersistenceGateway() @@ -100,8 +100,8 @@ describe('Get users in section', () => { it('retrieves the users in the judge section when the current user has the appropriate permissions', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, - userId: 'docketClerk', + role: ROLES.docketClerk, + userId: '5a797be2-a4b7-469f-9cfb-32b7f169d489', }); applicationContext .getPersistenceGateway() @@ -112,13 +112,13 @@ describe('Get users in section', () => { sectionToGet, }); expect(section.length).toEqual(2); - expect(section[0].userId).toEqual('judge@example.com'); + expect(section[0].name).toEqual('Test Judge 1'); }); it('returns unauthorizederror when the desired section is judge and current user does not have appropriate permissions', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '5a797be2-a4b7-469f-9cfb-32b7f169d489', }); applicationContext .getPersistenceGateway() diff --git a/shared/src/business/useCases/users/updateUserContactInformationInteractor.test.js b/shared/src/business/useCases/users/updateUserContactInformationInteractor.test.js index f7e20dd7d93..1551aadf6f7 100644 --- a/shared/src/business/useCases/users/updateUserContactInformationInteractor.test.js +++ b/shared/src/business/useCases/users/updateUserContactInformationInteractor.test.js @@ -8,11 +8,11 @@ const { const { updateUserContactInformationInteractor, } = require('./updateUserContactInformationInteractor'); -const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { MOCK_CASE } = require('../../../test/mockCase'); const { MOCK_USERS } = require('../../../test/mockUsers'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const fakeData = 'JVBERi0xLjEKJcKlwrHDqwoKMSAwIG9iagogIDw8IC9UeXBlIC9DYXRhbG9nCiAgICAgL1BhZ2VzIDIgMCBSCiAgPj4KZW5kb2JqCgoyIDAgb2JqCiAgPDwgL1R5cGUgL1BhZ2VzCiAgICAgL0tpZHMgWzMgMCBSXQogICAgIC9Db3VudCAxCiAgICAgL01lZGlhQm94IFswIDAgMzAwIDE0NF0KICA+PgplbmRvYmoKCjMgMCBvYmoKICA8PCAgL1R5cGUgL1BhZ2UKICAgICAgL1BhcmVudCAyIDAgUgogICAgICAvUmVzb3VyY2VzCiAgICAgICA8PCAvRm9udAogICAgICAgICAgIDw8IC9GMQogICAgICAgICAgICAgICA8PCAvVHlwZSAvRm9udAogICAgICAgICAgICAgICAgICAvU3VidHlwZSAvVHlwZTEKICAgICAgICAgICAgICAgICAgL0Jhc2VGb250IC9UaW1lcy1Sb21hbgogICAgICAgICAgICAgICA+PgogICAgICAgICAgID4+CiAgICAgICA+PgogICAgICAvQ29udGVudHMgNCAwIFIKICA+PgplbmRvYmoKCjQgMCBvYmoKICA8PCAvTGVuZ3RoIDg0ID4+CnN0cmVhbQogIEJUCiAgICAvRjEgMTggVGYKICAgIDUgODAgVGQKICAgIChDb25ncmF0aW9ucywgeW91IGZvdW5kIHRoZSBFYXN0ZXIgRWdnLikgVGoKICBFVAplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTggMDAwMDAgbiAKMDAwMDAwMDA3NyAwMDAwMCBuIAowMDAwMDAwMTc4IDAwMDAwIG4gCjAwMDAwMDA0NTcgMDAwMDAgbiAKdHJhaWxlcgogIDw8ICAvUm9vdCAxIDAgUgogICAgICAvU2l6ZSA1CiAgPj4Kc3RhcnR4cmVmCjU2NQolJUVPRgo='; @@ -99,7 +99,7 @@ describe('updateUserContactInformationInteractor', () => { irsPractitioners: [ { contact: {}, - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }, ], @@ -135,7 +135,7 @@ describe('updateUserContactInformationInteractor', () => { irsPractitioners: [ { contact: contactInfo, - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }, ], @@ -159,7 +159,7 @@ describe('updateUserContactInformationInteractor', () => { privatePractitioners: [ { contact: {}, - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }, ], @@ -170,11 +170,11 @@ describe('updateUserContactInformationInteractor', () => { privatePractitioners: [ { contact: {}, - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }, ], - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, { ...MOCK_CASE, @@ -182,11 +182,11 @@ describe('updateUserContactInformationInteractor', () => { privatePractitioners: [ { contact: {}, - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }, ], - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, ]; @@ -219,7 +219,7 @@ describe('updateUserContactInformationInteractor', () => { privatePractitioners: [ { contact: contactInfo, - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }, ], @@ -231,7 +231,7 @@ describe('updateUserContactInformationInteractor', () => { privatePractitioners: [ { contact: contactInfo, - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }, ], @@ -240,7 +240,7 @@ describe('updateUserContactInformationInteractor', () => { it('returns unauthorized error when user not authorized', async () => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'f7d90c05-f6cd-442c-a168-202db587f16f', }; diff --git a/shared/src/business/useCases/validateAddDeficiencyStatisticsInteractor.js b/shared/src/business/useCases/validateAddDeficiencyStatisticsInteractor.js new file mode 100644 index 00000000000..c1dc55448c3 --- /dev/null +++ b/shared/src/business/useCases/validateAddDeficiencyStatisticsInteractor.js @@ -0,0 +1,19 @@ +const { Statistic } = require('../entities/Statistic'); + +/** + * validateAddDeficiencyStatisticsInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {object} providers.statistic the statistic data to validate + * @returns {object} errors (null if no errors) + */ +exports.validateAddDeficiencyStatisticsInteractor = ({ + applicationContext, + statistic, +}) => { + const errors = new Statistic(statistic, { + applicationContext, + }).getFormattedValidationErrors(); + return errors || null; +}; diff --git a/shared/src/business/useCases/validateCaseDetailInteractor.test.js b/shared/src/business/useCases/validateCaseDetailInteractor.test.js index b3ca6fb56e0..8f4b99d167f 100644 --- a/shared/src/business/useCases/validateCaseDetailInteractor.test.js +++ b/shared/src/business/useCases/validateCaseDetailInteractor.test.js @@ -1,17 +1,20 @@ +const { + COUNTRY_TYPES, + PARTY_TYPES, + ROLES, +} = require('../entities/EntityConstants'); const { validateCaseDetailInteractor, } = require('./validateCaseDetailInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); const { Case } = require('../entities/cases/Case'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { MOCK_USERS } = require('../../test/mockUsers'); -const { User } = require('../entities/User'); const { VALIDATION_ERROR_MESSAGES } = Case; const contactPrimary = { address1: '123 Main St', city: 'Somewhere', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, name: 'Test Petitioner', phone: '1234567890', postalCode: '12345', @@ -72,8 +75,8 @@ describe('validate case detail', () => { docketNumber: '101-18', documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '9271f5ca-e7c9-40e8-b465-e970e22934e8', workItems: [], }, { @@ -81,15 +84,15 @@ describe('validate case detail', () => { docketNumber: '101-18', documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '9271f5ca-e7c9-40e8-b465-e970e22934e8', workItems: [], }, ], filingType: 'Myself', hasVerifiedIrsNotice: true, irsNoticeDate: new Date().toISOString(), - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitioners: [{ name: 'user' }], preferredTrialCity: 'Fresno, California', procedureType: 'Regular', @@ -134,8 +137,8 @@ describe('validate case detail', () => { docketNumber: '101-18', documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '9271f5ca-e7c9-40e8-b465-e970e22934e8', workItems: [], }, { @@ -143,15 +146,15 @@ describe('validate case detail', () => { docketNumber: '101-18', documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '9271f5ca-e7c9-40e8-b465-e970e22934e8', workItems: [], }, ], filingType: 'Other', hasVerifiedIrsNotice: true, irsNoticeDate: new Date().toISOString(), - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitioners: [{ name: 'user' }], preferredTrialCity: 'Fresno, California', procedureType: 'Regular', @@ -184,8 +187,8 @@ describe('validate case detail', () => { docketNumber: '101-18', documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '9271f5ca-e7c9-40e8-b465-e970e22934e8', workItems: [], }, { @@ -193,15 +196,15 @@ describe('validate case detail', () => { docketNumber: '101-18', documentId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', documentType: 'Petition', - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '9271f5ca-e7c9-40e8-b465-e970e22934e8', workItems: [], }, ], filingType: 'Other', hasVerifiedIrsNotice: false, irsNoticeDate: null, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, petitioners: [{ name: 'user' }], preferredTrialCity: 'Fresno, California', procedureType: 'Regular', diff --git a/shared/src/business/useCases/validatePetitionFromPaperInteractor.test.js b/shared/src/business/useCases/validatePetitionFromPaperInteractor.test.js index de3bef12bf7..c0a12b549f9 100644 --- a/shared/src/business/useCases/validatePetitionFromPaperInteractor.test.js +++ b/shared/src/business/useCases/validatePetitionFromPaperInteractor.test.js @@ -2,7 +2,7 @@ const { validatePetitionFromPaperInteractor, } = require('./validatePetitionFromPaperInteractor'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { Case } = require('../entities/cases/Case'); +const { PAYMENT_STATUS } = require('../entities/EntityConstants'); describe('validate petition from paper', () => { it('returns the expected errors object on an empty petition', () => { @@ -35,7 +35,7 @@ describe('validate petition from paper', () => { partyType: 'testing', petitionFile: {}, petitionFileSize: 100, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, procedureType: 'testing', receivedAt: new Date().toISOString(), }, diff --git a/shared/src/business/useCases/validatePetitionerInformationFormInteractor.test.js b/shared/src/business/useCases/validatePetitionerInformationFormInteractor.test.js index 466fb688c2e..ba150adc7e6 100644 --- a/shared/src/business/useCases/validatePetitionerInformationFormInteractor.test.js +++ b/shared/src/business/useCases/validatePetitionerInformationFormInteractor.test.js @@ -1,13 +1,13 @@ const { validatePetitionerInformationFormInteractor, } = require('./validatePetitionerInformationFormInteractor'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); +const { PARTY_TYPES } = require('../entities/EntityConstants'); describe('validatePetition', () => { it('returns the expected errors object when contactPrimary is missing fields', () => { const errors = validatePetitionerInformationFormInteractor({ contactPrimary: {}, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); expect(Object.keys(errors)).toEqual(['contactPrimary', 'contactSecondary']); @@ -28,7 +28,7 @@ describe('validatePetition', () => { state: 'TN', title: 'Executor', }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); expect(errors.contactPrimary).toBeNull(); @@ -58,7 +58,7 @@ describe('validatePetition', () => { state: 'TN', title: 'Executor', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(errors.contactPrimary).toBeNull(); diff --git a/shared/src/business/useCases/validatePrimaryContactInteractor.test.js b/shared/src/business/useCases/validatePrimaryContactInteractor.test.js index 863537374ea..4bd2f61f13b 100644 --- a/shared/src/business/useCases/validatePrimaryContactInteractor.test.js +++ b/shared/src/business/useCases/validatePrimaryContactInteractor.test.js @@ -2,6 +2,7 @@ const { validatePrimaryContactInteractor, } = require('./validatePrimaryContactInteractor'); const { ContactFactory } = require('../entities/contacts/ContactFactory'); +const { PARTY_TYPES } = require('../entities/EntityConstants'); describe('validatePrimaryContactInteractor', () => { it('runs validation on a contact with no invalid properties', async () => { @@ -18,7 +19,7 @@ describe('validatePrimaryContactInteractor', () => { state: 'MN', }; - const partyType = ContactFactory.PARTY_TYPES.petitioner; + const partyType = PARTY_TYPES.petitioner; const errors = validatePrimaryContactInteractor({ contactInfo: contactPrimary, @@ -42,7 +43,7 @@ describe('validatePrimaryContactInteractor', () => { state: 'MN', }; - const partyType = ContactFactory.PARTY_TYPES.petitioner; + const partyType = PARTY_TYPES.petitioner; const errors = validatePrimaryContactInteractor({ contactInfo: contactPrimary, diff --git a/shared/src/business/useCases/validateSecondaryContactInteractor.test.js b/shared/src/business/useCases/validateSecondaryContactInteractor.test.js index 4c27b5bd90e..ef705f3f59e 100644 --- a/shared/src/business/useCases/validateSecondaryContactInteractor.test.js +++ b/shared/src/business/useCases/validateSecondaryContactInteractor.test.js @@ -2,6 +2,7 @@ const { validateSecondaryContactInteractor, } = require('./validateSecondaryContactInteractor'); const { ContactFactory } = require('../entities/contacts/ContactFactory'); +const { PARTY_TYPES } = require('../entities/EntityConstants'); describe('validateSecondaryContactInteractor', () => { it('runs validation on a contact with no invalid properties', async () => { @@ -18,7 +19,7 @@ describe('validateSecondaryContactInteractor', () => { state: 'MN', }; - const partyType = ContactFactory.PARTY_TYPES.petitionerSpouse; + const partyType = PARTY_TYPES.petitionerSpouse; const errors = validateSecondaryContactInteractor({ contactInfo: contactSecondary, @@ -42,7 +43,7 @@ describe('validateSecondaryContactInteractor', () => { state: 'MN', }; - const partyType = ContactFactory.PARTY_TYPES.petitionerSpouse; + const partyType = PARTY_TYPES.petitionerSpouse; const errors = validateSecondaryContactInteractor({ contactInfo: contactSecondary, diff --git a/shared/src/business/useCases/workitems/assignWorkItemsInteractor.test.js b/shared/src/business/useCases/workitems/assignWorkItemsInteractor.test.js index 6583cb07ef3..17558ae09a5 100644 --- a/shared/src/business/useCases/workitems/assignWorkItemsInteractor.test.js +++ b/shared/src/business/useCases/workitems/assignWorkItemsInteractor.test.js @@ -2,15 +2,15 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); const { assignWorkItemsInteractor } = require('./assignWorkItemsInteractor'); -const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { omit } = require('lodash'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const MOCK_WORK_ITEM = { assigneeId: null, assigneeName: 'bob', caseId: 'e631d81f-a579-4de5-b8a8-b3f10ef619fd', - caseStatus: Case.STATUS_TYPES.generalDocket, + caseStatus: CASE_STATUS_TYPES.generalDocket, createdAt: '2018-12-27T18:06:02.971Z', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -59,7 +59,7 @@ describe('assignWorkItemsInteractor', () => { applicationContext.user = { name: 'bob', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }; let error; try { diff --git a/shared/src/business/useCases/workitems/completeWorkItemInteractor.test.js b/shared/src/business/useCases/workitems/completeWorkItemInteractor.test.js index 5c2e4c3ed64..0a206a094e7 100644 --- a/shared/src/business/useCases/workitems/completeWorkItemInteractor.test.js +++ b/shared/src/business/useCases/workitems/completeWorkItemInteractor.test.js @@ -2,7 +2,7 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); const { completeWorkItemInteractor } = require('./completeWorkItemInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('completeWorkItemInteractor', () => { let mockWorkItem = { @@ -22,7 +22,7 @@ describe('completeWorkItemInteractor', () => { const mockPetitionerUser = { name: 'Petitioner', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; diff --git a/shared/src/business/useCases/workitems/createWorkItemInteractor.test.js b/shared/src/business/useCases/workitems/createWorkItemInteractor.test.js index 275243f8f44..57318c1356e 100644 --- a/shared/src/business/useCases/workitems/createWorkItemInteractor.test.js +++ b/shared/src/business/useCases/workitems/createWorkItemInteractor.test.js @@ -1,8 +1,8 @@ -const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { createWorkItemInteractor } = require('./createWorkItemInteractor'); const { MOCK_CASE } = require('../../../test/mockCase'); const { MOCK_USERS } = require('../../../test/mockUsers'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('createWorkItem', () => { let createWorkItemStub; @@ -17,7 +17,7 @@ describe('createWorkItem', () => { environment: { stage: 'local' }, getCurrentUser: () => ({ name: 'Tax Payer', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }), getPersistenceGateway: () => ({ @@ -51,7 +51,7 @@ describe('createWorkItem', () => { const applicationContext = createApplicationContext({ getCurrentUser: () => ({ name: 'Docketclerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'a7d90c05-f6cd-442c-a168-202db587f16f', }), }); @@ -68,7 +68,7 @@ describe('createWorkItem', () => { assigneeId: 'b7d90c05-f6cd-442c-a168-202db587f16f', assigneeName: 'Docketclerk1', caseId: 'b54ba5a9-b37b-479d-9201-067ec6e335bb', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-18', docketNumberWithSuffix: '101-18', document: { @@ -96,7 +96,7 @@ describe('createWorkItem', () => { const applicationContext = createApplicationContext({ getCurrentUser: () => ({ name: 'Docketclerk', - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'a7d90c05-f6cd-442c-a168-202db587f16f', }), }); @@ -115,7 +115,7 @@ describe('createWorkItem', () => { assigneeId: 'b7d90c05-f6cd-442c-a168-202db587f16f', assigneeName: 'Docketclerk1', caseId: 'b54ba5a9-b37b-479d-9201-067ec6e335bb', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, docketNumber: '101-18', docketNumberWithSuffix: '101-18', document: { diff --git a/shared/src/business/useCases/workitems/forwardWorkItemInteractor.test.js b/shared/src/business/useCases/workitems/forwardWorkItemInteractor.test.js index 7f45d67f3a2..3f1338bbcc9 100644 --- a/shared/src/business/useCases/workitems/forwardWorkItemInteractor.test.js +++ b/shared/src/business/useCases/workitems/forwardWorkItemInteractor.test.js @@ -1,15 +1,15 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); -const { Case } = require('../../entities/cases/Case'); +const { CASE_STATUS_TYPES } = require('../../entities/EntityConstants'); const { forwardWorkItemInteractor } = require('./forwardWorkItemInteractor'); const { MOCK_CASE } = require('../../../../src/test/mockCase'); const { MOCK_USERS } = require('../../../test/mockUsers'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); const mockPetitionsClerk = { name: 'Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: 'c7d90c05-f6cd-442c-a168-202db587f16f', }; @@ -22,13 +22,13 @@ const mockCase = { documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', documentType: 'Proposed Stipulated Decision', processingStatus: 'pending', - userId: 'petitioner', + userId: '7ad8dcbc-5978-4a29-8c41-02422b66f410', workItems: [ { assigneeId: null, assigneeName: null, caseId: 'd3d92ca6-d9b3-4bd6-8328-e94a9fc36f88', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, createdAt: '2019-07-12T17:09:41.027Z', docketNumber: '106-19', docketNumberSuffix: null, @@ -56,7 +56,8 @@ const mockCase = { ], pk: 'work-item|c54ba5a9-b37b-479d-9201-067ec6e335bb', section: 'petitions', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', + sentByUserId: '7805d1ab-18d0-43ec-bafb-654e83405416', sk: 'work-item|c54ba5a9-b37b-479d-9201-067ec6e335bb', updatedAt: '2019-07-12T17:09:41.027Z', workItemId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -65,7 +66,7 @@ const mockCase = { assigneeId: null, assigneeName: null, caseId: 'd3d92ca6-d9b3-4bd6-8328-e94a9fc36f88', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, createdAt: '2019-07-12T17:09:41.027Z', docketNumber: '106-19', docketNumberSuffix: null, @@ -93,7 +94,8 @@ const mockCase = { ], pk: 'work-item|c54ba5a9-b37b-479d-9201-067ec6e335bb', section: 'petitions', - sentBy: '7805d1ab-18d0-43ec-bafb-654e83405416', + sentBy: 'Test Petitioner', + sentByUserId: '7805d1ab-18d0-43ec-bafb-654e83405416', sk: 'work-item|c54ba5a9-b37b-479d-9201-067ec6e335bb', updatedAt: '2019-07-12T17:09:41.027Z', workItemId: 'a54ba5a9-b37b-479d-9201-067ec6e335bb', @@ -186,7 +188,7 @@ describe('forwardWorkItemInteractor', () => { it('throws an error when an unauthorized user tries to access the use case', async () => { const mockTaxPayer = { name: 'Tax Payer', - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }; applicationContext.getCurrentUser.mockReturnValue(mockTaxPayer); diff --git a/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.js b/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.js index f461890aadb..966f451350f 100644 --- a/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.js +++ b/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.js @@ -1,7 +1,7 @@ const { DOCKET_SECTION, PETITIONS_SECTION, -} = require('../../entities/WorkQueue'); +} = require('../../entities/EntityConstants'); const { isAuthorized, ROLE_PERMISSIONS, diff --git a/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.test.js b/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.test.js index df3b81800b3..287a5021b88 100644 --- a/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.test.js +++ b/shared/src/business/useCases/workitems/getDocumentQCInboxForSectionInteractor.test.js @@ -4,7 +4,7 @@ const { const { getDocumentQCInboxForSectionInteractor, } = require('./getDocumentQCInboxForSectionInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getDocumentQCInboxForSectionInteractor', () => { let mockWorkItem = { @@ -22,7 +22,7 @@ describe('getDocumentQCInboxForSectionInteractor', () => { it('throws an error if the user does not have access to the work item', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext diff --git a/shared/src/business/useCases/workitems/getDocumentQCInboxForUserInteractor.test.js b/shared/src/business/useCases/workitems/getDocumentQCInboxForUserInteractor.test.js index 1d8af829b69..d1485839f3a 100644 --- a/shared/src/business/useCases/workitems/getDocumentQCInboxForUserInteractor.test.js +++ b/shared/src/business/useCases/workitems/getDocumentQCInboxForUserInteractor.test.js @@ -4,7 +4,7 @@ const { const { getDocumentQCInboxForUserInteractor, } = require('./getDocumentQCInboxForUserInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getDocumentQCInboxForUserInteractor', () => { let mockWorkItem = { @@ -22,7 +22,7 @@ describe('getDocumentQCInboxForUserInteractor', () => { it('throws an error if the user does not have access to the work item', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); applicationContext.getPersistenceGateway().getDocumentQCServedForSection = async () => diff --git a/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.js b/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.js index 1543cf923b7..4c1188a870b 100644 --- a/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.js +++ b/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.js @@ -2,8 +2,8 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const { WorkItem } = require('../../entities/WorkItem'); /** @@ -33,7 +33,7 @@ exports.getDocumentQCServedForSectionInteractor = async ({ }); const filteredWorkItems = workItems.filter(workItem => - user.role === User.ROLES.petitionsClerk ? !!workItem.section : true, + user.role === ROLES.petitionsClerk ? !!workItem.section : true, ); return WorkItem.validateRawCollection(filteredWorkItems, { diff --git a/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.test.js b/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.test.js index 0d9329ab0e5..cb6347fa1e8 100644 --- a/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.test.js +++ b/shared/src/business/useCases/workitems/getDocumentQCServedForSectionInteractor.test.js @@ -5,15 +5,15 @@ const { getDocumentQCServedForSectionInteractor, } = require('./getDocumentQCServedForSectionInteractor'); const { MOCK_USERS } = require('../../../test/mockUsers'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('getDocumentQCServedForSectionInteractor', () => { let user; beforeEach(() => { user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'a7d90c05-f6cd-442c-a168-202db587f16f', }; applicationContext.getCurrentUser.mockReturnValue(user); @@ -36,7 +36,7 @@ describe('getDocumentQCServedForSectionInteractor', () => { document: { sentBy: 'petitioner' }, isQC: true, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]; @@ -50,7 +50,7 @@ describe('getDocumentQCServedForSectionInteractor', () => { it('throws an error if the user does not have access to the work item', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'd7d90c05-f6cd-442c-a168-202db587f16f', }; applicationContext.getCurrentUser.mockReturnValue(user); @@ -87,7 +87,7 @@ describe('getDocumentQCServedForSectionInteractor', () => { sentBy: 'petitioner', }, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]); @@ -95,7 +95,7 @@ describe('getDocumentQCServedForSectionInteractor', () => { it('successfully returns the work item for a petitionsclerk', async () => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '4b423e1f-4eb2-4011-a845-873b82bee0a8', }; applicationContext.getCurrentUser.mockReturnValue(user); @@ -123,7 +123,7 @@ describe('getDocumentQCServedForSectionInteractor', () => { sentBy: 'petitioner', }, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]); diff --git a/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.js b/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.js index da1f80035f5..f6099b805de 100644 --- a/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.js +++ b/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.js @@ -2,8 +2,8 @@ const { isAuthorized, ROLE_PERMISSIONS, } = require('../../../authorization/authorizationClientService'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); const { WorkItem } = require('../../entities/WorkItem'); /** @@ -31,7 +31,7 @@ exports.getDocumentQCServedForUserInteractor = async ({ }); const filteredWorkItems = workItems.filter(workItem => - user.role === User.ROLES.petitionsClerk ? !!workItem.section : true, + user.role === ROLES.petitionsClerk ? !!workItem.section : true, ); return WorkItem.validateRawCollection(filteredWorkItems, { diff --git a/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.test.js b/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.test.js index cc9c4856661..14e8a2dd40c 100644 --- a/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.test.js +++ b/shared/src/business/useCases/workitems/getDocumentQCServedForUserInteractor.test.js @@ -4,15 +4,15 @@ const { const { getDocumentQCServedForUserInteractor, } = require('./getDocumentQCServedForUserInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('getDocumentQCServedForUserInteractor', () => { let user; beforeEach(() => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; applicationContext.getCurrentUser.mockReturnValue(user); @@ -35,7 +35,7 @@ describe('getDocumentQCServedForUserInteractor', () => { document: { sentBy: 'petitioner' }, isQC: true, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]; @@ -47,7 +47,7 @@ describe('getDocumentQCServedForUserInteractor', () => { it('throws an error if the user does not have access to the work item', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; @@ -83,7 +83,7 @@ describe('getDocumentQCServedForUserInteractor', () => { docketNumberWithSuffix: '101-18S', document: { sentBy: 'petitioner' }, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]); @@ -91,7 +91,7 @@ describe('getDocumentQCServedForUserInteractor', () => { it('successfully returns the work items for a docket clerk', async () => { user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketclerk', }; @@ -118,7 +118,7 @@ describe('getDocumentQCServedForUserInteractor', () => { docketNumberWithSuffix: '101-18S', document: { sentBy: 'petitioner' }, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]); diff --git a/shared/src/business/useCases/workitems/getInboxMessagesForSectionInteractor.test.js b/shared/src/business/useCases/workitems/getInboxMessagesForSectionInteractor.test.js index 3df488f5a37..8e0c0e48ece 100644 --- a/shared/src/business/useCases/workitems/getInboxMessagesForSectionInteractor.test.js +++ b/shared/src/business/useCases/workitems/getInboxMessagesForSectionInteractor.test.js @@ -4,11 +4,11 @@ const { const { getInboxMessagesForSectionInteractor, } = require('./getInboxMessagesForSectionInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getInboxMessagesForSectionInteractor', () => { const mockPetitionsClerk = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsClerk', }; @@ -47,7 +47,7 @@ describe('getInboxMessagesForSectionInteractor', () => { it('throws an error if the user does not have access to the work item', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }); diff --git a/shared/src/business/useCases/workitems/getInboxMessagesForUserInteractor.test.js b/shared/src/business/useCases/workitems/getInboxMessagesForUserInteractor.test.js index f13050fdeaa..279aacc6e11 100644 --- a/shared/src/business/useCases/workitems/getInboxMessagesForUserInteractor.test.js +++ b/shared/src/business/useCases/workitems/getInboxMessagesForUserInteractor.test.js @@ -4,11 +4,11 @@ const { const { getInboxMessagesForUserInteractor, } = require('./getInboxMessagesForUserInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getInboxMessagesForUserInteractor', () => { const mockPetitionerUser = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; diff --git a/shared/src/business/useCases/workitems/getSentMessagesForSectionInteractor.test.js b/shared/src/business/useCases/workitems/getSentMessagesForSectionInteractor.test.js index ff43deb6b4f..ddf856cee02 100644 --- a/shared/src/business/useCases/workitems/getSentMessagesForSectionInteractor.test.js +++ b/shared/src/business/useCases/workitems/getSentMessagesForSectionInteractor.test.js @@ -4,8 +4,8 @@ const { const { getSentMessagesForSectionInteractor, } = require('./getSentMessagesForSectionInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('getSentMessagesForSectionInteractor', () => { const sentMessagesForSectionMock = [ @@ -26,7 +26,7 @@ describe('getSentMessagesForSectionInteractor', () => { document: { sentBy: 'petitioner' }, isQC: false, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]; @@ -39,7 +39,7 @@ describe('getSentMessagesForSectionInteractor', () => { it('throws an error if the user does not have access to the work item', async () => { const mockPetitionerUser = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; applicationContext.getCurrentUser.mockReturnValue(mockPetitionerUser); @@ -54,8 +54,8 @@ describe('getSentMessagesForSectionInteractor', () => { it('successfully returns the work item for a docket clerk', async () => { const mockDocketClerkUser = { - role: User.ROLES.docketClerk, - userId: 'docketClerk', + role: ROLES.docketClerk, + userId: 'bf143814-1354-4c4c-be9e-ca8144a15117', }; applicationContext.getCurrentUser.mockReturnValue(mockDocketClerkUser); diff --git a/shared/src/business/useCases/workitems/getSentMessagesForUserInteractor.test.js b/shared/src/business/useCases/workitems/getSentMessagesForUserInteractor.test.js index 9a90fd2a5d6..02638c9b4b5 100644 --- a/shared/src/business/useCases/workitems/getSentMessagesForUserInteractor.test.js +++ b/shared/src/business/useCases/workitems/getSentMessagesForUserInteractor.test.js @@ -4,8 +4,8 @@ const { const { getSentMessagesForUserInteractor, } = require('./getSentMessagesForUserInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('getSentMessagesForUserInteractor', () => { let user; @@ -28,14 +28,14 @@ describe('getSentMessagesForUserInteractor', () => { document: { sentBy: 'petitioner' }, isQC: false, messages: [], - section: 'irsBatchSection', + section: 'docket', sentBy: 'docketclerk', }, ]; beforeEach(() => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; applicationContext.getCurrentUser.mockReturnValue(user); @@ -43,7 +43,7 @@ describe('getSentMessagesForUserInteractor', () => { it('throws an error if the user does not have access to the work item', async () => { user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; applicationContext.getCurrentUser.mockReturnValue(user); diff --git a/shared/src/business/useCases/workitems/getWorkItemInteractor.test.js b/shared/src/business/useCases/workitems/getWorkItemInteractor.test.js index aeb5da2be4e..b411e8e4936 100644 --- a/shared/src/business/useCases/workitems/getWorkItemInteractor.test.js +++ b/shared/src/business/useCases/workitems/getWorkItemInteractor.test.js @@ -2,7 +2,7 @@ const { applicationContext, } = require('../../test/createTestApplicationContext'); const { getWorkItemInteractor } = require('./getWorkItemInteractor'); -const { User } = require('../../entities/User'); +const { ROLES } = require('../../entities/EntityConstants'); describe('getWorkItemInteractor', () => { let mockWorkItem = { @@ -21,12 +21,12 @@ describe('getWorkItemInteractor', () => { }; const mockPetitionerUser = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: 'petitioner', }; const mockDocketClerkUser = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketclerk', }; diff --git a/shared/src/business/useCases/workitems/setWorkItemAsReadInteractor.test.js b/shared/src/business/useCases/workitems/setWorkItemAsReadInteractor.test.js index 93febd602e9..8da75eece3e 100644 --- a/shared/src/business/useCases/workitems/setWorkItemAsReadInteractor.test.js +++ b/shared/src/business/useCases/workitems/setWorkItemAsReadInteractor.test.js @@ -4,8 +4,8 @@ const { const { setWorkItemAsReadInteractor, } = require('./setWorkItemAsReadInteractor'); +const { ROLES } = require('../../entities/EntityConstants'); const { UnauthorizedError } = require('../../../errors/errors'); -const { User } = require('../../entities/User'); describe('setWorkItemAsReadInteractor', () => { let user; @@ -34,7 +34,7 @@ describe('setWorkItemAsReadInteractor', () => { it('returns the expected result', async () => { user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'petitionsclerk', }; applicationContext diff --git a/shared/src/business/useCases/workitems/validateForwardMessageInteractor.test.js b/shared/src/business/useCases/workitems/validateForwardMessageInteractor.test.js index f8948b06867..f43ae091855 100644 --- a/shared/src/business/useCases/workitems/validateForwardMessageInteractor.test.js +++ b/shared/src/business/useCases/workitems/validateForwardMessageInteractor.test.js @@ -51,12 +51,12 @@ describe('validateForwardMessageInteractor', () => { applicationContext, message: { assigneeId: 'c54ba5a9-b37b-479d-9201-067ec6e335bb', - section: VALIDATION_ERROR_MESSAGES.section, }, }); expect(errors).toEqual({ forwardMessage: VALIDATION_ERROR_MESSAGES.forwardMessage, + section: VALIDATION_ERROR_MESSAGES.section, }); }); diff --git a/shared/src/business/utilities/DateHandler.js b/shared/src/business/utilities/DateHandler.js index d3d62f8262a..099de0427d3 100644 --- a/shared/src/business/utilities/DateHandler.js +++ b/shared/src/business/utilities/DateHandler.js @@ -14,6 +14,10 @@ const FORMATS = { YYYYMMDD: 'YYYY-MM-DD', }; +const PATTERNS = { + 'H:MM': /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, // hour can be specified with either one OR two digits. +}; + const USTC_TZ = 'America/New_York'; const isStringISOFormatted = dateString => { @@ -193,6 +197,7 @@ const calculateDifferenceInDays = (timeStamp1, timeStamp2) => { module.exports = { FORMATS, + PATTERNS, calculateDifferenceInDays, calculateISODate, calendarDatesCompared, diff --git a/shared/src/business/utilities/DateHandler.test.js b/shared/src/business/utilities/DateHandler.test.js index a4eb896d408..8e6d242b889 100644 --- a/shared/src/business/utilities/DateHandler.test.js +++ b/shared/src/business/utilities/DateHandler.test.js @@ -1,8 +1,26 @@ const DateHandler = require('./DateHandler'); +const { FORMATS, PATTERNS } = DateHandler; const { getTimestampSchema } = require('../../utilities/dateSchema'); const joiStrictTimestamp = getTimestampSchema(); describe('DateHandler', () => { + describe('pattern matcher', () => { + describe('H:MM', () => { + it('matches valid times', () => { + expect(PATTERNS['H:MM'].test('00:00')).toBeTruthy(); + expect(PATTERNS['H:MM'].test('9:00')).toBeTruthy(); + expect(PATTERNS['H:MM'].test('09:00')).toBeTruthy(); + expect(PATTERNS['H:MM'].test('10:30')).toBeTruthy(); + expect(PATTERNS['H:MM'].test('23:59')).toBeTruthy(); + }); + it('rejects invalid times', () => { + expect(PATTERNS['H:MM'].test('7:60')).toBeFalsy(); + expect(PATTERNS['H:MM'].test('29:59')).toBeFalsy(); + expect(PATTERNS['H:MM'].test('30:00')).toBeFalsy(); + }); + }); + }); + describe('prepareDateFromString', () => { it("Creates a new moment object for 'now' when given no inputs'", () => { const myMoment = DateHandler.prepareDateFromString(); @@ -129,7 +147,7 @@ describe('DateHandler', () => { // now confirm it converts "back" to originally desired time const formattedInEastern = DateHandler.formatDateString( startOfDay, - DateHandler.FORMATS.DATE_TIME, + FORMATS.DATE_TIME, ); expect(formattedInEastern).toEqual('04/07/20 12:00 am'); // the stroke of midnight }); @@ -147,7 +165,7 @@ describe('DateHandler', () => { // now confirm it converts "back" to originally desired time const formattedInEastern = DateHandler.formatDateString( endOfDay, - DateHandler.FORMATS.DATE_TIME, + FORMATS.DATE_TIME, ); expect(formattedInEastern).toEqual('04/07/20 11:59 pm'); // the moment before midnight the next day }); diff --git a/shared/src/business/utilities/aggregateCommonQueryParams.js b/shared/src/business/utilities/aggregateCommonQueryParams.js index 570da029a91..ec12951f599 100644 --- a/shared/src/business/utilities/aggregateCommonQueryParams.js +++ b/shared/src/business/utilities/aggregateCommonQueryParams.js @@ -1,4 +1,4 @@ -const { CaseSearch } = require('../entities/cases/CaseSearch'); +const { CASE_SEARCH_MIN_YEAR } = require('../entities/EntityConstants'); /** * aggregateCommonQueryParams @@ -117,7 +117,7 @@ const aggregateCommonQueryParams = ({ }); } if (yearFiledMin || yearFiledMax) { - const yearMin = yearFiledMin || CaseSearch.CASE_SEARCH_MIN_YEAR; + const yearMin = yearFiledMin || CASE_SEARCH_MIN_YEAR; const yearMax = yearFiledMax || applicationContext.getUtilities().formatNow('YYYY'); diff --git a/shared/src/business/utilities/aggregateCommonQueryParams.test.js b/shared/src/business/utilities/aggregateCommonQueryParams.test.js index 5dea6903067..eff6326ca51 100644 --- a/shared/src/business/utilities/aggregateCommonQueryParams.test.js +++ b/shared/src/business/utilities/aggregateCommonQueryParams.test.js @@ -1,7 +1,10 @@ +const { + CASE_SEARCH_MIN_YEAR, + COUNTRY_TYPES, + US_STATES, +} = require('../entities/EntityConstants'); const { aggregateCommonQueryParams } = require('./aggregateCommonQueryParams'); const { applicationContext } = require('../test/createTestApplicationContext'); -const { CaseSearch } = require('../entities/cases/CaseSearch'); -const { ContactFactory } = require('../entities/contacts/ContactFactory'); const { formatNow } = require('./DateHandler'); describe('aggregateCommonQueryParams', () => { @@ -108,7 +111,7 @@ describe('aggregateCommonQueryParams', () => { it('should include search params for countryType if present in query', () => { const queryParams = { applicationContext, - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, }; const result = aggregateCommonQueryParams(queryParams); @@ -120,14 +123,12 @@ describe('aggregateCommonQueryParams', () => { should: [ { match: { - 'contactPrimary.M.countryType.S': - ContactFactory.COUNTRY_TYPES.DOMESTIC, + 'contactPrimary.M.countryType.S': COUNTRY_TYPES.DOMESTIC, }, }, { match: { - 'contactSecondary.M.countryType.S': - ContactFactory.COUNTRY_TYPES.DOMESTIC, + 'contactSecondary.M.countryType.S': COUNTRY_TYPES.DOMESTIC, }, }, ], @@ -144,7 +145,7 @@ describe('aggregateCommonQueryParams', () => { it('should include search params for petitionerState if present in query', () => { const queryParams = { applicationContext, - petitionerState: ContactFactory.US_STATES.AR, + petitionerState: US_STATES.AR, }; const result = aggregateCommonQueryParams(queryParams); @@ -156,12 +157,12 @@ describe('aggregateCommonQueryParams', () => { should: [ { match: { - 'contactPrimary.M.state.S': ContactFactory.US_STATES.AR, + 'contactPrimary.M.state.S': US_STATES.AR, }, }, { match: { - 'contactSecondary.M.state.S': ContactFactory.US_STATES.AR, + 'contactSecondary.M.state.S': US_STATES.AR, }, }, ], @@ -241,7 +242,7 @@ describe('aggregateCommonQueryParams', () => { range: { 'receivedAt.S': { format: 'yyyy', - gte: `${CaseSearch.CASE_SEARCH_MIN_YEAR}||/y`, + gte: `${CASE_SEARCH_MIN_YEAR}||/y`, lte: '2019||/y', }, }, diff --git a/shared/src/business/utilities/aggregatePartiesForService.js b/shared/src/business/utilities/aggregatePartiesForService.js index 225406e5603..8d09235f4b4 100644 --- a/shared/src/business/utilities/aggregatePartiesForService.js +++ b/shared/src/business/utilities/aggregatePartiesForService.js @@ -1,7 +1,7 @@ const { setServiceIndicatorsForCase, } = require('./setServiceIndicatorsForCase'); -const { SERVICE_INDICATOR_TYPES } = require('../entities/cases/CaseConstants'); +const { SERVICE_INDICATOR_TYPES } = require('../entities/EntityConstants'); /** * aggregatePartiesForService diff --git a/shared/src/business/utilities/createPractitionerUser.test.js b/shared/src/business/utilities/createPractitionerUser.test.js index 8bf80e6a588..02acb7624fa 100644 --- a/shared/src/business/utilities/createPractitionerUser.test.js +++ b/shared/src/business/utilities/createPractitionerUser.test.js @@ -1,6 +1,6 @@ const { applicationContext } = require('../test/createTestApplicationContext'); const { createPractitionerUser } = require('./createPractitionerUser'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); describe('createPractitionerUser', () => { const mockAdmissionsDate = new Date('1876/02/19'); @@ -15,7 +15,7 @@ describe('createPractitionerUser', () => { lastName: 'IRSPractitioner', originalBarState: 'CA', practitionerType: 'Attorney', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }; const result = await createPractitionerUser({ @@ -38,7 +38,7 @@ describe('createPractitionerUser', () => { lastName: 'IRSPractitioner', originalBarState: 'CA', practitionerType: 'Attorney', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }; const result = await createPractitionerUser({ diff --git a/shared/src/business/utilities/documentGenerators.js b/shared/src/business/utilities/documentGenerators.js index 0c148b15526..3192981ba97 100644 --- a/shared/src/business/utilities/documentGenerators.js +++ b/shared/src/business/utilities/documentGenerators.js @@ -10,6 +10,34 @@ const { generatePrintableDocketRecordTemplate, } = require('./generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate'); +const addressLabelCoverSheet = async ({ applicationContext, data }) => { + const addressLabelCoverSheetTemplate = reactTemplateGenerator({ + componentName: 'AddressLabelCoverSheet', + data, + }); + + const pdfContentHtml = await generateHTMLTemplateForPDF({ + applicationContext, + // TODO: Remove main prop when index.pug can be refactored to remove header logic + content: { main: addressLabelCoverSheetTemplate }, + options: { + overwriteMain: true, + title: '', + }, + }); + + const pdf = await applicationContext + .getUseCases() + .generatePdfFromHtmlInteractor({ + applicationContext, + contentHtml: pdfContentHtml, + displayHeaderFooter: false, + overwriteHeader: true, + }); + + return pdf; +}; + const changeOfAddress = async ({ applicationContext, content }) => { const pdfContentHtml = await generateChangeOfAddressTemplate({ applicationContext, @@ -39,21 +67,69 @@ const changeOfAddress = async ({ applicationContext, content }) => { return pdf; }; +const coverSheet = async ({ applicationContext, data }) => { + const coverSheetTemplate = reactTemplateGenerator({ + componentName: 'CoverSheet', + data, + }); + + const pdfContentHtml = await generateHTMLTemplateForPDF({ + applicationContext, + // TODO: Remove main prop when index.pug can be refactored to remove header logic + content: { main: coverSheetTemplate }, + options: { + overwriteMain: true, + title: 'Cover Sheet', + }, + }); + + let footerHtml = ''; + if (data.dateServed) { + footerHtml = reactTemplateGenerator({ + componentName: 'DateServedFooter', + data: { + dateServed: data.dateServed, + }, + }); + } + + const pdf = await applicationContext + .getUseCases() + .generatePdfFromHtmlInteractor({ + applicationContext, + contentHtml: pdfContentHtml, + displayHeaderFooter: true, + docketNumber: data.docketNumberWithSuffix, + footerHtml, + headerHtml: '', + overwriteHeader: true, + }); + + return pdf; +}; + const docketRecord = async ({ applicationContext, data }) => { const pdfContentHtml = await generatePrintableDocketRecordTemplate({ applicationContext, data, }); - const docketNumber = data.caseDetail.docketNumberWithSuffix; - const headerHtml = reactTemplateGenerator({ componentName: 'PageMetaHeaderDocket', data: { - docketNumber, + docketNumber: data.docketNumberWithSuffix, + }, + }); + + const footerHtml = reactTemplateGenerator({ + componentName: 'DatePrintedFooter', + data: { + datePrinted: applicationContext.getUtilities().formatNow('MM/DD/YYYY'), }, }); + const docketNumber = data.caseDetail.docketNumberWithSuffix; + const pdf = await applicationContext .getUseCases() .generatePdfFromHtmlInteractor({ @@ -61,6 +137,7 @@ const docketRecord = async ({ applicationContext, data }) => { contentHtml: pdfContentHtml, displayHeaderFooter: true, docketNumber, + footerHtml, headerHtml, overwriteHeader: true, }); @@ -78,7 +155,7 @@ const noticeOfDocketChange = async ({ applicationContext, data }) => { filingsAndProceedings, } = data; - const reactStandingPretrialOrderTemplate = reactTemplateGenerator({ + const NoticeOfDocketChangeTemplate = reactTemplateGenerator({ componentName: 'NoticeOfDocketChange', data: { docketEntryIndex, @@ -95,7 +172,7 @@ const noticeOfDocketChange = async ({ applicationContext, data }) => { const pdfContentHtml = await generateHTMLTemplateForPDF({ applicationContext, // TODO: Remove main prop when index.pug can be refactored to remove header logic - content: { main: reactStandingPretrialOrderTemplate }, + content: { main: NoticeOfDocketChangeTemplate }, options: { overwriteMain: true, title: 'Notice of Docket Change', @@ -114,8 +191,99 @@ const noticeOfDocketChange = async ({ applicationContext, data }) => { return pdf; }; +const noticeOfReceiptOfPetition = async ({ applicationContext, data }) => { + const reactNoticeReceiptPetitionTemplate = reactTemplateGenerator({ + componentName: 'NoticeOfReceiptOfPetition', + data, + }); + + const pdfContentHtml = await generateHTMLTemplateForPDF({ + applicationContext, + // TODO: Remove main prop when index.pug can be refactored to remove header logic + content: { main: reactNoticeReceiptPetitionTemplate }, + options: { + overwriteMain: true, + title: 'Notice of Receipt of Petition', + }, + }); + + const headerHtml = reactTemplateGenerator({ + componentName: 'PageMetaHeaderDocket', + data: { + docketNumber: data.docketNumberWithSuffix, + }, + }); + + const pdf = await applicationContext + .getUseCases() + .generatePdfFromHtmlInteractor({ + applicationContext, + contentHtml: pdfContentHtml, + displayHeaderFooter: true, + docketNumber: data.docketNumberWithSuffix, + headerHtml, + }); + + return pdf; +}; + +const order = async ({ applicationContext, data }) => { + const { + caseCaptionExtension, + caseTitle, + docketNumberWithSuffix, + orderContent, + orderTitle, + signatureText, + } = data; + + const reactOrderTemplate = reactTemplateGenerator({ + componentName: 'Order', + data: { + options: { + caseCaptionExtension, + caseTitle, + docketNumberWithSuffix, + }, + orderContent, + orderTitle, + signatureText, + }, + }); + + const pdfContentHtml = await generateHTMLTemplateForPDF({ + applicationContext, + // TODO: Remove main prop when index.pug can be refactored to remove header logic + content: { main: reactOrderTemplate }, + options: { + overwriteMain: true, + title: orderTitle, + }, + }); + + const headerHtml = reactTemplateGenerator({ + componentName: 'PageMetaHeaderDocket', + data: { + docketNumber: docketNumberWithSuffix, + }, + }); + + const pdf = await applicationContext + .getUseCases() + .generatePdfFromHtmlInteractor({ + applicationContext, + contentHtml: pdfContentHtml, + displayHeaderFooter: true, + docketNumber: docketNumberWithSuffix, + headerHtml, + overwriteHeader: true, + }); + + return pdf; +}; + const pendingReport = async ({ applicationContext, data }) => { - const { docketNumberWithSuffix, pendingItems, subtitle } = data; + const { pendingItems, subtitle } = data; const pendingReportTemplate = reactTemplateGenerator({ componentName: 'PendingReport', @@ -155,7 +323,6 @@ const pendingReport = async ({ applicationContext, data }) => { applicationContext, contentHtml: pdfContentHtml, displayHeaderFooter: true, - docketNumber: docketNumberWithSuffix, footerHtml, headerHtml, overwriteHeader: true, @@ -198,6 +365,59 @@ const receiptOfFiling = async ({ applicationContext, data }) => { applicationContext, // TODO: Remove main prop when index.pug can be refactored to remove header logic content: { main: reactReceiptOfFilingTemplate }, + options: { + overwriteMain: true, + title: 'Receipt of Filing', + }, + }); + + const headerHtml = reactTemplateGenerator({ + componentName: 'PageMetaHeaderDocket', + data: { + docketNumber: docketNumberWithSuffix, + }, + }); + + const pdf = await applicationContext + .getUseCases() + .generatePdfFromHtmlInteractor({ + applicationContext, + contentHtml: pdfContentHtml, + displayHeaderFooter: true, + docketNumber: docketNumberWithSuffix, + headerHtml, + overwriteHeader: true, + }); + + return pdf; +}; + +const standingPretrialNotice = async ({ applicationContext, data }) => { + const { + caseCaptionExtension, + caseTitle, + docketNumberWithSuffix, + footerDate, + trialInfo, + } = data; + + const reactStandingPretrialNoticeTemplate = reactTemplateGenerator({ + componentName: 'StandingPretrialNotice', + data: { + footerDate, + options: { + caseCaptionExtension, + caseTitle, + docketNumberWithSuffix, + }, + trialInfo, + }, + }); + + const pdfContentHtml = await generateHTMLTemplateForPDF({ + applicationContext, + // TODO: Remove main prop when index.pug can be refactored to remove header logic + content: { main: reactStandingPretrialNoticeTemplate }, options: { overwriteMain: true, title: 'Standing Pre-trial Order', @@ -334,12 +554,118 @@ const caseInventoryReport = async ({ applicationContext, data }) => { return pdf; }; +const trialCalendar = async ({ applicationContext, data }) => { + const { cases, sessionDetail } = data; + + const trialCalendarTemplate = reactTemplateGenerator({ + componentName: 'TrialCalendar', + data: { + cases, + sessionDetail, + }, + }); + + const pdfContentHtml = await generateHTMLTemplateForPDF({ + applicationContext, + // TODO: Remove main prop when index.pug can be refactored to remove header logic + content: { main: trialCalendarTemplate }, + options: { + overwriteMain: true, + title: 'Trial Calendar', + }, + }); + + const headerHtml = reactTemplateGenerator({ + componentName: 'ReportsMetaHeader', + data: { + headerTitle: `Trial Calendar: ${sessionDetail.trialLocation} - ${sessionDetail.startDate} ${sessionDetail.sessionType}`, + }, + }); + + const footerHtml = reactTemplateGenerator({ + componentName: 'DatePrintedFooter', + data: { + datePrinted: applicationContext.getUtilities().formatNow('MM/DD/YYYY'), + }, + }); + + const pdf = await applicationContext + .getUseCases() + .generatePdfFromHtmlInteractor({ + applicationContext, + contentHtml: pdfContentHtml, + displayHeaderFooter: true, + footerHtml, + headerHtml, + overwriteHeader: true, + }); + + return pdf; +}; + +const trialSessionPlanningReport = async ({ applicationContext, data }) => { + const { locationData, previousTerms, term } = data; + + const trialSessionPlanningReportTemplate = reactTemplateGenerator({ + componentName: 'TrialSessionPlanningReport', + data: { + locationData, + previousTerms, + term, + }, + }); + + const pdfContentHtml = await generateHTMLTemplateForPDF({ + applicationContext, + // TODO: Remove main prop when index.pug can be refactored to remove header logic + content: { main: trialSessionPlanningReportTemplate }, + options: { + overwriteMain: true, + title: 'Trial Session Planning Report', + }, + }); + + const headerHtml = reactTemplateGenerator({ + componentName: 'ReportsMetaHeader', + data: { + headerTitle: `Trial Session Planning Report: ${term}`, + }, + }); + + const footerHtml = reactTemplateGenerator({ + componentName: 'DatePrintedFooter', + data: { + datePrinted: applicationContext.getUtilities().formatNow('MM/DD/YYYY'), + }, + }); + + const pdf = await applicationContext + .getUseCases() + .generatePdfFromHtmlInteractor({ + applicationContext, + contentHtml: pdfContentHtml, + displayHeaderFooter: true, + footerHtml, + headerHtml, + overwriteHeader: true, + }); + + return pdf; +}; + module.exports = { + addressLabelCoverSheet, caseInventoryReport, changeOfAddress, + coverSheet, docketRecord, noticeOfDocketChange, + noticeOfReceiptOfPetition, + order, pendingReport, receiptOfFiling, + standingPretrialNotice, standingPretrialOrder, + trialCalendar, + trialSessionPlanningReport, }; diff --git a/shared/src/business/utilities/documentGenerators.test.js b/shared/src/business/utilities/documentGenerators.test.js index 1e0a2f2b9b6..79a0d742214 100644 --- a/shared/src/business/utilities/documentGenerators.test.js +++ b/shared/src/business/utilities/documentGenerators.test.js @@ -8,13 +8,20 @@ const { const { getChromiumBrowser } = require('./getChromiumBrowser'); const { + addressLabelCoverSheet, caseInventoryReport, changeOfAddress, + coverSheet, docketRecord, noticeOfDocketChange, + noticeOfReceiptOfPetition, + order, pendingReport, receiptOfFiling, + standingPretrialNotice, standingPretrialOrder, + trialCalendar, + trialSessionPlanningReport, } = require('./documentGenerators'); describe('documentGenerators', () => { @@ -51,6 +58,36 @@ describe('documentGenerators', () => { ); } }); + + describe('addressLabelCoverSheet', () => { + it('generates an Address Lable Cover Sheet document', async () => { + const pdf = await addressLabelCoverSheet({ + applicationContext, + data: { + address1: '123 Some Street', + city: 'Some City', + countryName: 'USA', + docketNumberWithSuffix: '123-45S', + name: 'Test Person', + postalCode: '89890', + state: 'ZZ', + }, + }); + + // Do not write PDF when running on CircleCI + if (process.env.PDF_OUTPUT) { + writePdfFile('Address_Label_Cover_Sheet', pdf); + expect(applicationContext.getChromiumBrowser).toHaveBeenCalled(); + } + + expect( + applicationContext.getUseCases().generatePdfFromHtmlInteractor, + ).toHaveBeenCalled(); + expect(applicationContext.getNodeSass).toHaveBeenCalled(); + expect(applicationContext.getPug).toHaveBeenCalled(); + }); + }); + describe('caseInventoryReport', () => { it('generates a Case Inventory Report document', async () => { const pdf = await caseInventoryReport({ @@ -129,6 +166,38 @@ describe('documentGenerators', () => { }); }); + describe('coverSheet', () => { + it('Generates a CoverSheet document', async () => { + const pdf = await coverSheet({ + applicationContext, + data: { + caseCaptionExtension: 'Petitioner', + caseTitle: 'Test Person', + certificateOfService: true, + dateFiledLodged: '01/01/2020', + dateFiledLodgedLabel: 'Filed', + dateReceived: '01/02/2020', + dateServed: '01/03/2020', + docketNumberWithSuffix: '123-45S', + documentTitle: 'Petition', + electronicallyFiled: true, + }, + }); + + // Do not write PDF when running on CircleCI + if (process.env.PDF_OUTPUT) { + writePdfFile('CoverSheet', pdf); + expect(applicationContext.getChromiumBrowser).toHaveBeenCalled(); + } + + expect( + applicationContext.getUseCases().generatePdfFromHtmlInteractor, + ).toHaveBeenCalled(); + expect(applicationContext.getNodeSass).toHaveBeenCalled(); + expect(applicationContext.getPug).toHaveBeenCalled(); + }); + }); + describe('docketRecord', () => { it('Generates a Printable Docket Record document', async () => { const pdf = await docketRecord({ @@ -222,7 +291,7 @@ describe('documentGenerators', () => { }); describe('noticeOfDocketChange', () => { - it('generates a Standing Pre-trial Order document', async () => { + it('generates a Notice of Docket Change document', async () => { const pdf = await noticeOfDocketChange({ applicationContext, data: { @@ -251,6 +320,83 @@ describe('documentGenerators', () => { }); }); + describe('noticeOfReceiptOfPetition', () => { + it('generates a Notice of Receipt of Petition document', async () => { + const pdf = await noticeOfReceiptOfPetition({ + applicationContext, + data: { + address: { + address1: '123 Some St.', + city: 'Somecity', + countryName: '', + name: 'Test Petitioner', + postalCode: '80008', + state: 'ZZ', + }, + caseCaptionExtension: 'Petitioner(s)', + caseTitle: 'Test Petitioner', + docketNumberWithSuffix: '123-45S', + preferredTrialCity: 'Birmingham, AL', + receivedAtFormatted: 'December 1, 2019', + servedDate: 'June 3, 2020', + }, + }); + + // Do not write PDF when running on CircleCI + if (process.env.PDF_OUTPUT) { + writePdfFile('Notice_Receipt_Petition', pdf); + expect(applicationContext.getChromiumBrowser).toHaveBeenCalled(); + } + + expect( + applicationContext.getUseCases().generatePdfFromHtmlInteractor, + ).toHaveBeenCalled(); + expect(applicationContext.getNodeSass).toHaveBeenCalled(); + expect(applicationContext.getPug).toHaveBeenCalled(); + }); + }); + + describe('standingPretrialNotice', () => { + it('generates a Standing Pre-trial Notice document', async () => { + const pdf = await standingPretrialNotice({ + applicationContext, + data: { + caseCaptionExtension: 'Petitioner(s)', + caseTitle: 'Test Petitioner', + docketNumberWithSuffix: '123-45S', + footerDate: '02/02/20', + trialInfo: { + address1: '123 Some St.', + address2: '3rd Floor', + address3: 'Suite B', + city: 'Some City', + courthouseName: 'Hall of Justice', + fullStartDate: 'Friday May 8, 2020', + judge: { + name: 'Test Judge', + }, + postalCode: '12345', + startDay: 'Friday', + startTime: '10:00am', + state: 'TEST STATE', + }, + }, + }); + + // Do not write PDF when running on CircleCI + if (process.env.PDF_OUTPUT) { + writePdfFile('Standing_Pretrial_Notice', pdf); + expect(applicationContext.getChromiumBrowser).toHaveBeenCalled(); + } + + expect( + applicationContext.getUseCases().generatePdfFromHtmlInteractor, + ).toHaveBeenCalled(); + expect(applicationContext.getNodeSass).toHaveBeenCalled(); + expect(applicationContext.getPug).toHaveBeenCalled(); + }); + }); + describe('standingPretrialOrder', () => { it('generates a Standing Pre-trial Order document', async () => { const pdf = await standingPretrialOrder({ @@ -285,6 +431,45 @@ describe('documentGenerators', () => { }); }); + describe('order', () => { + it('generates a Standing Pre-trial Order document', async () => { + const pdf = await order({ + applicationContext, + data: { + caseCaptionExtension: 'Petitioner(s)', + caseTitle: 'Test Petitioner', + docketNumberWithSuffix: '123-45S', + orderContent: `

Upon due consideration ofthe parties' joint motion to remand, filed December 30, 2019, and the parties' joint motion for continuance, filed December 30, 2019, it is

+ +

ORDERED that the joint motion for continuance is granted in that thesecases are stricken for trial from the Court's January 27, 2020, Los Angeles, California, trial session. It is further

+ +

ORDERED that the joint motion to remand to respondent's Appeals Office is granted and these cases are + remanded to respondent's Appeals Office for a supplemental collection due process hearing. It is further

+ +

ORDERED that respondent shall offer petitioners an administrative hearing at respondent's Appeals Office + located closest to petitioners' residence (or at such other place as may be mutually agreed upon) at a + reasonable and mutually agreed upon date and time, but no later than April 1, 2020. It is further

+ +

ORDERED that each party shall, on or before April 15, 2020, file with the Court, and serve on the other party, a report regarding the then present status of these cases. It is further

`, + orderTitle: 'ORDER', + signatureText: 'Test Signature', + }, + }); + + // Do not write PDF when running on CircleCI + if (process.env.PDF_OUTPUT) { + writePdfFile('Order', pdf); + expect(applicationContext.getChromiumBrowser).toHaveBeenCalled(); + } + + expect( + applicationContext.getUseCases().generatePdfFromHtmlInteractor, + ).toHaveBeenCalled(); + expect(applicationContext.getNodeSass).toHaveBeenCalled(); + expect(applicationContext.getPug).toHaveBeenCalled(); + }); + }); + describe('pendingReport', () => { it('generates a Pending Report document', async () => { const pdf = await pendingReport({ @@ -292,35 +477,35 @@ describe('documentGenerators', () => { data: { pendingItems: [ { + associatedJudgeFormatted: 'Chief Judge', caseTitle: 'Test Petitioner', - dateFiled: '02/02/20', docketNumberWithSuffix: '123-45S', - filingsAndProceedings: 'Order', - judge: 'Chief Judge', + formattedFiledDate: '02/02/20', + formattedName: 'Order', status: 'closed', }, { + associatedJudgeFormatted: 'Chief Judge', caseTitle: 'Test Petitioner', - dateFiled: '02/22/20', docketNumberWithSuffix: '123-45S', - filingsAndProceedings: 'Motion for a New Trial', - judge: 'Chief Judge', + formattedFiledDate: '02/22/20', + formattedName: 'Motion for a New Trial', status: 'closed', }, { + associatedJudgeFormatted: 'Chief Judge', caseTitle: 'Other Petitioner', - dateFiled: '03/03/20', docketNumberWithSuffix: '321-45S', - filingsAndProceedings: 'Order', - judge: 'Chief Judge', + formattedFiledDate: '03/03/20', + formattedName: 'Order', status: 'closed', }, { + associatedJudgeFormatted: 'Chief Judge', caseTitle: 'Other Petitioner', - dateFiled: '03/23/20', docketNumberWithSuffix: '321-45S', - filingsAndProceedings: 'Order to Show Cause', - judge: 'Chief Judge', + formattedFiledDate: '03/23/20', + formattedName: 'Order to Show Cause', status: 'closed', }, ], @@ -414,4 +599,111 @@ describe('documentGenerators', () => { expect(applicationContext.getPug).toHaveBeenCalled(); }); }); + + describe('trialCalendar', () => { + it('generates a Trial Calendar document', async () => { + const pdf = await trialCalendar({ + applicationContext, + data: { + cases: [ + { + caseTitle: 'Paul Simon', + docketNumber: '123-45S', + petitionerCounsel: ['Ben Matlock', 'Atticus Finch'], + respondentCounsel: ['Sonny Crockett', 'Ricardo Tubbs'], + }, + { + caseTitle: 'Art Garfunkel', + docketNumber: '234-56', + petitionerCounsel: ['Mick Haller'], + respondentCounsel: ['Joy Falotico'], + }, + ], + sessionDetail: { + address1: '123 Some Street', + address2: 'Suite B', + courtReporter: 'Lois Lane', + courthouseName: 'Test Courthouse', + formattedCityStateZip: 'New York, NY 10108', + irsCalendarAdministrator: 'iCalRS Admin', + judge: 'Joseph Dredd', + notes: + 'The one with the velour shirt is definitely looking at me funny.', + sessionType: 'Hybrid', + startDate: 'May 1, 2020', + startTime: '10:00am', + trialClerk: 'Clerky McGee', + trialLocation: 'New York City, New York', + }, + }, + }); + + // Do not write PDF when running on CircleCI + if (process.env.PDF_OUTPUT) { + writePdfFile('Trial_Calendar', pdf); + expect(applicationContext.getChromiumBrowser).toHaveBeenCalled(); + } + + expect( + applicationContext.getUseCases().generatePdfFromHtmlInteractor, + ).toHaveBeenCalled(); + expect(applicationContext.getNodeSass).toHaveBeenCalled(); + expect(applicationContext.getPug).toHaveBeenCalled(); + }); + }); + + describe('trialSessionPlanningReport', () => { + it('generates a Trial Session Planning Report document', async () => { + const pdf = await trialSessionPlanningReport({ + applicationContext, + data: { + locationData: [ + { + allCaseCount: 5, + previousTermsData: [['(S) Buch', '(R) Cohen'], [], []], + regularCaseCount: 3, + smallCaseCount: 2, + stateAbbreviation: 'AR', + trialCityState: 'Little Rock, AR', + }, + { + allCaseCount: 2, + previousTermsData: [[], [], []], + regularCaseCount: 1, + smallCaseCount: 1, + stateAbbreviation: 'AL', + trialCityState: 'Mobile, AL', + }, + ], + previousTerms: [ + { + name: 'Fall', + year: '2019', + }, + { + name: 'Spring', + year: '2019', + }, + { + name: 'Winter', + year: '2019', + }, + ], + term: 'Winter 2020', + }, + }); + + // Do not write PDF when running on CircleCI + if (process.env.PDF_OUTPUT) { + writePdfFile('Trial_Session_Planning_Report', pdf); + expect(applicationContext.getChromiumBrowser).toHaveBeenCalled(); + } + + expect( + applicationContext.getUseCases().generatePdfFromHtmlInteractor, + ).toHaveBeenCalled(); + expect(applicationContext.getNodeSass).toHaveBeenCalled(); + expect(applicationContext.getPug).toHaveBeenCalled(); + }); + }); }); diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateCoverPagePdf.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateCoverPagePdf.js deleted file mode 100644 index 3204e0cbc79..00000000000 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateCoverPagePdf.js +++ /dev/null @@ -1,147 +0,0 @@ -const ustcLogoBufferBase64 = require('../../../../static/images/ustc_seal.png_'); - -/** - * generateCoverPagePdf - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {string} providers.content the html content for the cover sheet - * @returns {Buffer} the pdf as a binary buffer - */ -exports.generateCoverPagePdf = async ({ applicationContext, content }) => { - let browser = null; - let result = null; - const horizontalMargin = 40; - - try { - browser = await applicationContext.getChromiumBrowser(); - let page = await browser.newPage(); - - const headerTemplate = ''; - const fontSizeDefault = 15; - const fontSizeCaption = 15; - const fontSizeTitle = 18; - - const mainContent = ` -
- -
-
-
-
- Received -
- ${content.dateReceived} -
-
-
-
- ${content.dateFiledLodgedLabel} -
- ${content.dateFiledLodged} -
-
-
- -
-
- ${content.caseTitle} -

-
- ${content.caseCaptionExtension} -

- v. -
-
- Commissioner of Internal Revenue, -

-
- Respondent -
-
-
-
- ${content.electronicallyFiled ? 'Electronically Filed
' : ''} - ${content.mailingDate ? content.mailingDate + '
' : ''} -
- ${content.docketNumber} -
-
-
-
- -
- ${content.documentTitle} -
-
`; - - const footerTemplate = ` - - - - - -
- ${content.certificateOfService} - -
- ${content.dateServed} -
-
-

- - - `; - - await page.setContent(mainContent); - - result = await page.pdf({ - displayHeaderFooter: true, - footerTemplate, - format: 'Letter', - headerTemplate, - margin: { - bottom: '200px', - top: '0px', - }, - printBackground: true, - }); - } catch (error) { - applicationContext.logger.error(error); - throw error; - } finally { - if (browser !== null) { - await browser.close(); - } - } - return result; -}; diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateHTMLTemplateForPDF.test.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateHTMLTemplateForPDF.test.js index d680861424c..09cdc481812 100644 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateHTMLTemplateForPDF.test.js +++ b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateHTMLTemplateForPDF.test.js @@ -1,11 +1,11 @@ const createApplicationContext = require('../../../../../web-api/src/applicationContext'); const { generateHTMLTemplateForPDF } = require('./generateHTMLTemplateForPDF'); const applicationContext = createApplicationContext({}); -const { Case } = require('../../entities/cases/Case'); +const { CASE_CAPTION_POSTFIX } = require('../../entities/EntityConstants'); describe('generateHTMLTemplateForPDF', () => { const content = { - caseCaptionWithPostfix: `Test Case Caption ${Case.CASE_CAPTION_POSTFIX}`, + caseCaptionWithPostfix: `Test Case Caption ${CASE_CAPTION_POSTFIX}`, docketNumberWithSuffix: '123-45S', main: '
Test Main Content
', }; @@ -25,7 +25,7 @@ describe('generateHTMLTemplateForPDF', () => { expect(result.indexOf('')).toBe(0); expect(result.indexOf('U.S. Tax Court')).toBeGreaterThan(-1); expect( - result.indexOf(`Test Case Caption ${Case.CASE_CAPTION_POSTFIX}`), + result.indexOf(`Test Case Caption ${CASE_CAPTION_POSTFIX}`), ).toBeGreaterThan(-1); expect(result.indexOf('123-45S')).toBeGreaterThan(-1); expect(result.indexOf('
Test Main Content
')).toBeGreaterThan(-1); diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateNoticeOfTrialIssuedTemplate.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateNoticeOfTrialIssuedTemplate.js index f2bea1dfd84..c7b36a73463 100644 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateNoticeOfTrialIssuedTemplate.js +++ b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateNoticeOfTrialIssuedTemplate.js @@ -5,6 +5,7 @@ const { formatNow, } = require('../DateHandler'); const { Case } = require('../../entities/cases/Case'); +const { CASE_CAPTION_POSTFIX } = require('../../entities/EntityConstants'); const { generateHTMLTemplateForPDF } = require('./generateHTMLTemplateForPDF'); /** @@ -45,7 +46,7 @@ const generateNoticeOfTrialIssuedTemplate = async ({ }); const templateContent = { - caseCaptionWithPostfix: `${caseCaption} ${Case.CASE_CAPTION_POSTFIX}`, + caseCaptionWithPostfix: `${caseCaption} ${CASE_CAPTION_POSTFIX}`, docketNumberWithSuffix, main, }; diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.js index 483a42e5a6b..e239768208d 100644 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.js +++ b/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.js @@ -1,4 +1,4 @@ -const { ContactFactory } = require('../../entities/contacts/ContactFactory'); +const { COUNTRY_TYPES } = require('../../entities/EntityConstants'); const { generateHTMLTemplateForPDF } = require('./generateHTMLTemplateForPDF'); const { reactTemplateGenerator } = require('./reactTemplateGenerator'); @@ -26,7 +26,7 @@ const generatePrintableDocketRecordTemplate = async ({ componentName: 'DocketRecord', data: { caseDetail, - countryTypes: ContactFactory.COUNTRY_TYPES, + countryTypes: COUNTRY_TYPES, entries, options: { caseCaptionExtension, diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.test.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.test.js index f7b3998ff88..55b7e0d518e 100644 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.test.js +++ b/shared/src/business/utilities/generateHTMLTemplateForPDF/generatePrintableDocketRecordTemplate.test.js @@ -2,7 +2,7 @@ const createApplicationContext = require('../../../../../web-api/src/application const { generatePrintableDocketRecordTemplate, } = require('./generatePrintableDocketRecordTemplate'); -const { Case } = require('../../entities/cases/Case'); +const { CASE_CAPTION_POSTFIX } = require('../../entities/EntityConstants'); const applicationContext = createApplicationContext({}); @@ -29,7 +29,7 @@ describe('generatePrintableDocketRecordTemplate', () => { expect(result.indexOf('')).toBe(0); expect(result.indexOf(data.caseTitle)).toBeGreaterThan(-1); expect(result.indexOf(data.caseCaptionExtension)).toBeGreaterThan(-1); - expect(result.indexOf(Case.CASE_CAPTION_POSTFIX)).toBeGreaterThan(-1); + expect(result.indexOf(CASE_CAPTION_POSTFIX)).toBeGreaterThan(-1); expect(result.indexOf(data.docketNumberWithSuffix)).toBeGreaterThan(-1); expect(result.indexOf('
')).toBeGreaterThan(-1); diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialCalendarTemplate.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialCalendarTemplate.js deleted file mode 100644 index 9bee62d5463..00000000000 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialCalendarTemplate.js +++ /dev/null @@ -1,301 +0,0 @@ -const { generateHTMLTemplateForPDF } = require('./generateHTMLTemplateForPDF'); - -/** - * HTML template generator for trial calendar PDF views - * - * @param {object} deconstructed function arguments - * @param {object} deconstructed.applicationContext object that contains all the context specific methods - * @param {object} deconstructed.content content to be injected into the template - * @returns {string} hydrated HTML content in string form - */ -const generateTrialCalendarTemplate = async ({ - applicationContext, - content, -}) => { - const { formattedTrialSessionDetails, openCases } = content; - - const renderCases = item => { - return ` - - ${item.docketNumberWithSuffix} - - ${item.caseTitle} - - ${item.privatePractitioners - .map(practitioner => practitioner.name) - .join('
')} - - - ${item.irsPractitioners - .map(respondent => respondent.name) - .join('
')} - - `; - }; - - const renderTrialCalendar = () => { - return ` -
-
-

United States Tax Court

-

${ - formattedTrialSessionDetails.trialLocation - }

-

- ${formattedTrialSessionDetails.formattedStartDateFull} - ${formattedTrialSessionDetails.sessionType} -

-
- -
-
-
- Trial Information -
- -
-
-

Start Time

-

${formattedTrialSessionDetails.formattedStartTime}

-
-
-

Location

- ${ - formattedTrialSessionDetails.noLocationEntered - ? '

No location entered

' - : '' - } -

${formattedTrialSessionDetails.courthouseName || ''}

-

- - ${formattedTrialSessionDetails.address1 || ''} - - - ${formattedTrialSessionDetails.address2 || ''} - - - ${formattedTrialSessionDetails.formattedCityStateZip || ''} - -

-
-
-
-
-
-
- Assignments -
-
-
-

Judge

-

${formattedTrialSessionDetails.formattedJudge}

- -

Court Reporter

-

${formattedTrialSessionDetails.formattedCourtReporter}

-
-
-

Trial Clerk

-

${ - formattedTrialSessionDetails.formattedTrialClerk - }

- -

IRS Calendar Administrator

-

${ - formattedTrialSessionDetails.formattedIrsCalendarAdministrator - }

-
-
-
-
- -
-
- Session Notes -
-
-

${formattedTrialSessionDetails.notes || ''}

-
-
- -

Open Cases (${openCases.length})

- - - - - - - - - - - - ${openCases.map(renderCases).join('')} - -
Docket no.Case titlePetitioner counselRespondent counsel
- `; - }; - - const main = renderTrialCalendar(); - - const templateContent = { - main, - }; - - const options = { - overwriteMain: true, - styles: ` - body { - margin: 35px 50px 10px 50px; - font-family: sans-serif; - font-size: 10px; - } - - h1 { - padding-top: 10px; - margin-bottom: 0px; - font-size: 24px; - line-height: 15px; - } - - h2 { - margin-top: 0px; - /* padding-bottom: 15px; */ - font-size: 20px; - font-weight: normal; - } - - h3 { - margin-top: 0px; - font-family: sans-serif; - font-size: 14px; - font-weight: normal; - } - - .bold { - font-weight: bold; - } - - .text-center { - text-align: center; - } - - table { - width: 100%; - border: 1px solid #ccc; - -webkit-border-horizontal-spacing: 0px; - - border-spacing: 0; - -webkit-border-vertical-spacing: 0px; - } - - th, - td { - border-bottom: 1px solid #ccc; - text-align: left; - vertical-align: top; - } - - td { - padding: 12px 8px; - line-height: 13px; - vertical-align: top; - } - - th { - padding: 8px 10px 10px 8px; - white-space: nowrap; - } - - th { - background-color: #f0f0f0; - } - - .grid-container { - display: grid; - grid-gap: 10px; - grid-template-columns: 50% 50%; - } - - .grid-container-main { - display: grid; - grid-template-columns: 49% 2% 49%; - } - - .mb-1 { - margin-bottom: 10px; - } - - .mb-2 { - margin-bottom: 20px; - } - - h4 { - margin-bottom: 0px; - } - - p { - margin-top: 4px; - } - - .address-line { - display: block; - } - - .court-header { - margin-bottom: 30px; - font-family: serif; - } - - .panel { - border: 1px solid #ccc; - margin: 15px 0 5px 0; - } - - .header { - padding: 8px 10px; - border-bottom: 1px solid #ccc; - background: #f0f0f0; - font-size: 10px; - font-weight: bold; - } - - .open-cases { - font-size: 12px; - } - - .content { - min-height: 15px; - padding: 0 10px 10px 10px; - } - - .content.notes { - padding-top: 12px; - } - - .mb-4 { - margin-bottom: 40px; - } - - .pr-1 { - padding-right: 10px; - } - - @page { - margin-bottom: 55px; - } - - @page :first { - margin-top: 0; - } - `, - title: 'Change of Contact Information', - }; - - return await generateHTMLTemplateForPDF({ - applicationContext, - content: templateContent, - options, - }); -}; - -module.exports = { generateTrialCalendarTemplate }; diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialCalendarTemplate.test.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialCalendarTemplate.test.js deleted file mode 100644 index b6c29c78705..00000000000 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialCalendarTemplate.test.js +++ /dev/null @@ -1,24 +0,0 @@ -const { - generateTrialCalendarTemplate, -} = require('./generateTrialCalendarTemplate'); - -const createApplicationContext = require('../../../../../web-api/src/applicationContext'); -const applicationContext = createApplicationContext({}); - -describe('generateTrialCalendarTemplate', () => { - const content = { - formattedTrialSessionDetails: { - formattedStartDateFull: '10/11/12 11:00 PM', - trialLocation: 'Mobile, Alabama', - }, - openCases: [], - }; - - it('generates a trial calendar', async () => { - const result = await generateTrialCalendarTemplate({ - applicationContext, - content, - }); - expect(result.indexOf('10/11/12 11:00 PM')).toBeGreaterThan(-1); - }); -}); diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialSessionPlanningReportTemplate.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialSessionPlanningReportTemplate.js deleted file mode 100644 index 324aa36b71f..00000000000 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialSessionPlanningReportTemplate.js +++ /dev/null @@ -1,103 +0,0 @@ -const { capitalize } = require('lodash'); -const { generateHTMLTemplateForPDF } = require('./generateHTMLTemplateForPDF'); - -/** - * HTML template generator for printable trial session planning report PDF views - * - * @param {object} deconstructed function arguments - * @param {object} deconstructed.applicationContext object that contains all the context specific methods - * @param {object} deconstructed.content content to be injected into the template - * @returns {string} hydrated HTML content in string form - */ -const generateTrialSessionPlanningReportTemplate = async ({ - applicationContext, - content, -}) => { - const { previousTerms, rows, selectedTerm, selectedYear } = content; - - const contentRows = rows.map(row => { - return ` - - ${row.stateAbbreviation} - ${row.trialCityState} - ${row.allCaseCount} - ${row.smallCaseCount} - ${row.regularCaseCount} - ${ - row.previousTermsData[0].join('
') || - '
' - } - ${ - row.previousTermsData[1].join('
') || - '
' - } - ${ - row.previousTermsData[2].join('
') || - '
' - } - - `; - }); - - const templateContent = { - main: ` -
-
-

United States Tax Court

-

Trial Session Planning Report - ${capitalize( - selectedTerm, - )} ${selectedYear}

-
- - - - - - - - - - - - - - - ${contentRows.join('')} - -
StateLocationAllSmallRegular${capitalize(previousTerms[0].term)} ${previousTerms[0].year}${capitalize(previousTerms[1].term)} ${previousTerms[1].year}${capitalize(previousTerms[2].term)} ${previousTerms[2].year}
- `, - }; - - const options = { - overwriteMain: true, - styles: ` - @page { - margin: 1.75cm 0cm 1.5cm; - size: 8.5in 11in; - } - @page :first { - margin-top: 1cm; - margin-bottom: 2cm; - } - .calendar-icon { - width: 12px; - height: 12px; - background: url('data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhciIgZGF0YS1pY29uPSJjYWxlbmRhci10aW1lcyIgY2xhc3M9InN2Zy1pbmxpbmUtLWZhIGZhLWNhbGVuZGFyLXRpbWVzIGZhLXctMTQiIHJvbGU9ImltZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNDQ4IDUxMiI+PHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMzExLjcgMzc0LjdsLTE3IDE3Yy00LjcgNC43LTEyLjMgNC43LTE3IDBMMjI0IDMzNy45bC01My43IDUzLjdjLTQuNyA0LjctMTIuMyA0LjctMTcgMGwtMTctMTdjLTQuNy00LjctNC43LTEyLjMgMC0xN2w1My43LTUzLjctNTMuNy01My43Yy00LjctNC43LTQuNy0xMi4zIDAtMTdsMTctMTdjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDUzLjcgNTMuNyA1My43LTUzLjdjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDE3IDE3YzQuNyA0LjcgNC43IDEyLjMgMCAxN0wyNTcuOSAzMDRsNTMuNyA1My43YzQuOCA0LjcgNC44IDEyLjMuMSAxN3pNNDQ4IDExMnYzNTJjMCAyNi41LTIxLjUgNDgtNDggNDhINDhjLTI2LjUgMC00OC0yMS41LTQ4LTQ4VjExMmMwLTI2LjUgMjEuNS00OCA0OC00OGg0OFYxMmMwLTYuNiA1LjQtMTIgMTItMTJoNDBjNi42IDAgMTIgNS40IDEyIDEydjUyaDEyOFYxMmMwLTYuNiA1LjQtMTIgMTItMTJoNDBjNi42IDAgMTIgNS40IDEyIDEydjUyaDQ4YzI2LjUgMCA0OCAyMS41IDQ4IDQ4em0tNDggMzQ2VjE2MEg0OHYyOThjMCAzLjMgMi43IDYgNiA2aDM0MGMzLjMgMCA2LTIuNyA2LTZ6Ij48L3BhdGg+PC9zdmc+'); - background-size: 12px 12px; - } - - tr { - break-inside: avoid !important; - } - `, - title: 'Trial Session Planning Report', - }; - - return await generateHTMLTemplateForPDF({ - applicationContext, - content: templateContent, - options, - }); -}; - -module.exports = { generateTrialSessionPlanningReportTemplate }; diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialSessionPlanningReportTemplate.test.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialSessionPlanningReportTemplate.test.js deleted file mode 100644 index 1c383b9aa9d..00000000000 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/generateTrialSessionPlanningReportTemplate.test.js +++ /dev/null @@ -1,42 +0,0 @@ -const { - generateTrialSessionPlanningReportTemplate, -} = require('./generateTrialSessionPlanningReportTemplate'); - -const createApplicationContext = require('../../../../../web-api/src/applicationContext'); -const applicationContext = createApplicationContext({}); - -describe('generateTrialSessionPlanningReportTemplate', () => { - const content = { - previousTerms: [ - { term: 'fall', year: '2020' }, - { term: 'spring', year: '2020' }, - { term: 'winter', year: '2019' }, - ], - rows: [ - { - allCaseCount: 4, - previousTermsData: [['(S) Ashford'], ['(S) Buch', '(R) Armen'], []], - regularCaseCount: 2, - smallCaseCount: 2, - stateAbbreviation: 'AL', - trialCityState: 'Birmingham, Alabama', - }, - ], - selectedTerm: 'winter', - selectedYear: '2020', - }; - - it('generates a trial session planning report', async () => { - const result = await generateTrialSessionPlanningReportTemplate({ - applicationContext, - content, - }); - - expect(result.indexOf('(S) Buch
(R) Armen')).toBeGreaterThan( - -1, - ); - expect( - result.indexOf('
'), - ).toBeGreaterThan(-1); - }); -}); diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/index.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/index.js index 279f90e9201..0f22b9b64b9 100644 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/index.js +++ b/shared/src/business/utilities/generateHTMLTemplateForPDF/index.js @@ -7,12 +7,6 @@ const { const { generatePrintableDocketRecordTemplate, } = require('./generatePrintableDocketRecordTemplate'); -const { - generateTrialCalendarTemplate, -} = require('./generateTrialCalendarTemplate'); -const { - generateTrialSessionPlanningReportTemplate, -} = require('./generateTrialSessionPlanningReportTemplate'); const { generateHTMLTemplateForPDF } = require('./generateHTMLTemplateForPDF'); module.exports = { @@ -20,6 +14,4 @@ module.exports = { generateHTMLTemplateForPDF, generateNoticeOfTrialIssuedTemplate, generatePrintableDocketRecordTemplate, - generateTrialCalendarTemplate, - generateTrialSessionPlanningReportTemplate, }; diff --git a/shared/src/business/utilities/generateHTMLTemplateForPDF/reactTemplateGenerator.js b/shared/src/business/utilities/generateHTMLTemplateForPDF/reactTemplateGenerator.js index e62703f7f6b..b409a032c06 100644 --- a/shared/src/business/utilities/generateHTMLTemplateForPDF/reactTemplateGenerator.js +++ b/shared/src/business/utilities/generateHTMLTemplateForPDF/reactTemplateGenerator.js @@ -5,21 +5,33 @@ require('@babel/register')({ }); // Documents +const { + AddressLabelCoverSheet, +} = require('../pdfGenerator/documentTemplates/AddressLabelCoverSheet.jsx'); const { CaseInventoryReport, } = require('../pdfGenerator/documentTemplates/CaseInventoryReport.jsx'); const { ChangeOfAddress, } = require('../pdfGenerator/documentTemplates/ChangeOfAddress.jsx'); +const { + CoverSheet, +} = require('../pdfGenerator/documentTemplates/CoverSheet.jsx'); const { DatePrintedFooter, } = require('../pdfGenerator/components/DatePrintedFooter.jsx'); +const { + DateServedFooter, +} = require('../pdfGenerator/components/DateServedFooter.jsx'); const { DocketRecord, } = require('../pdfGenerator/documentTemplates/DocketRecord.jsx'); const { NoticeOfDocketChange, } = require('../pdfGenerator/documentTemplates/NoticeOfDocketChange.jsx'); +const { + NoticeOfReceiptOfPetition, +} = require('../pdfGenerator/documentTemplates/NoticeOfReceiptOfPetition.jsx'); const { PageMetaHeaderDocket, } = require('../pdfGenerator/components/PageMetaHeaderDocket.jsx'); @@ -32,9 +44,19 @@ const { const { ReportsMetaHeader, } = require('../pdfGenerator/components/ReportsMetaHeader.jsx'); +const { + StandingPretrialNotice, +} = require('../pdfGenerator/documentTemplates/StandingPretrialNotice.jsx'); const { StandingPretrialOrder, } = require('../pdfGenerator/documentTemplates/StandingPretrialOrder.jsx'); +const { + TrialCalendar, +} = require('../pdfGenerator/documentTemplates/TrialCalendar.jsx'); +const { + TrialSessionPlanningReport, +} = require('../pdfGenerator/documentTemplates/TrialSessionPlanningReport.jsx'); +const { Order } = require('../pdfGenerator/documentTemplates/Order.jsx'); // Emails const { @@ -48,18 +70,26 @@ const React = require('react'); const ReactDOM = require('react-dom/server'); const components = { + AddressLabelCoverSheet, CaseInventoryReport, ChangeOfAddress, + CoverSheet, DatePrintedFooter, + DateServedFooter, DocketRecord, DocumentService, NoticeOfDocketChange, + NoticeOfReceiptOfPetition, + Order, PageMetaHeaderDocket, PendingReport, PetitionService, ReceiptOfFiling, ReportsMetaHeader, + StandingPretrialNotice, StandingPretrialOrder, + TrialCalendar, + TrialSessionPlanningReport, }; const reactTemplateGenerator = ({ componentName, data = {} }) => { diff --git a/shared/src/business/utilities/getCaseCaptionMeta.js b/shared/src/business/utilities/getCaseCaptionMeta.js index 580b13856dd..2ffcc266e6b 100644 --- a/shared/src/business/utilities/getCaseCaptionMeta.js +++ b/shared/src/business/utilities/getCaseCaptionMeta.js @@ -1,4 +1,5 @@ const { Case } = require('../entities/cases/Case'); +const { CASE_CAPTION_POSTFIX } = require('../entities/EntityConstants'); /** * Gets case caption parts from case data @@ -8,7 +9,6 @@ const { Case } = require('../entities/cases/Case'); */ const getCaseCaptionMeta = caseDetail => { - const { CASE_CAPTION_POSTFIX } = Case; const { caseCaption } = caseDetail; const caseTitle = Case.getCaseTitle(caseDetail.caseCaption); diff --git a/shared/src/business/utilities/getCaseCaptionMeta.test.js b/shared/src/business/utilities/getCaseCaptionMeta.test.js index c87691929d7..382d248c10d 100644 --- a/shared/src/business/utilities/getCaseCaptionMeta.test.js +++ b/shared/src/business/utilities/getCaseCaptionMeta.test.js @@ -1,4 +1,4 @@ -const { Case } = require('../entities/cases/Case'); +const { CASE_CAPTION_POSTFIX } = require('../entities/EntityConstants'); const { getCaseCaptionMeta } = require('./getCaseCaptionMeta'); describe('getCaseCaptionMeta', () => { @@ -7,9 +7,9 @@ describe('getCaseCaptionMeta', () => { caseCaption: 'Eve Brewer, Petitioner', }; expect(getCaseCaptionMeta(caseDetail)).toMatchObject({ - CASE_CAPTION_POSTFIX: Case.CASE_CAPTION_POSTFIX, + CASE_CAPTION_POSTFIX: CASE_CAPTION_POSTFIX, caseCaption: 'Eve Brewer, Petitioner', - caseCaptionWithPostfix: `Eve Brewer, Petitioner ${Case.CASE_CAPTION_POSTFIX}`, + caseCaptionWithPostfix: `Eve Brewer, Petitioner ${CASE_CAPTION_POSTFIX}`, }); }); diff --git a/shared/src/business/utilities/getFormattedCaseDetail.js b/shared/src/business/utilities/getFormattedCaseDetail.js index 145588548cd..ff102d57077 100644 --- a/shared/src/business/utilities/getFormattedCaseDetail.js +++ b/shared/src/business/utilities/getFormattedCaseDetail.js @@ -3,12 +3,17 @@ const { calendarDatesCompared, createISODateString, } = require('./DateHandler'); +const { + CASE_STATUS_TYPES, + COURT_ISSUED_EVENT_CODES, + PAYMENT_STATUS, + TRANSCRIPT_EVENT_CODE, +} = require('../entities/EntityConstants'); const { Case } = require('../entities/cases/Case'); const { cloneDeep, isEmpty } = require('lodash'); -const { Document } = require('../entities/Document'); -const { User } = require('../entities/User'); +const { ROLES } = require('../entities/EntityConstants'); -const courtIssuedDocumentTypes = Document.COURT_ISSUED_EVENT_CODES.map( +const courtIssuedDocumentTypes = COURT_ISSUED_EVENT_CODES.map( courtIssuedDoc => courtIssuedDoc.documentType, ); @@ -58,7 +63,7 @@ const formatDocument = (applicationContext, document) => { result.isNotServedCourtIssuedDocument = result.isCourtIssuedDocument && !result.servedAt; - result.isTranscript = result.eventCode === Document.TRANSCRIPT_EVENT_CODE; + result.isTranscript = result.eventCode === TRANSCRIPT_EVENT_CODE; result.qcWorkItemsUntouched = !!qcWorkItems.length && @@ -70,7 +75,7 @@ const formatDocument = (applicationContext, document) => { if (result.servedParties && result.servedParties.length > 0) { if ( result.servedParties.length === 1 && - result.servedParties[0].role === User.ROLES.irsSuperuser + result.servedParties[0].role === ROLES.irsSuperuser ) { result.servedPartiesCode = 'R'; } else { @@ -95,7 +100,7 @@ const formatDocketRecord = (applicationContext, docketRecord) => { const TRANSCRIPT_AGE_DAYS_MIN = 90; const documentMeetsAgeRequirements = document => { - const transcriptCodes = [Document.TRANSCRIPT_EVENT_CODE]; + const transcriptCodes = [TRANSCRIPT_EVENT_CODE]; const isTranscript = transcriptCodes.includes(document.eventCode); if (!isTranscript) return true; const availableOnDate = calculateISODate({ @@ -259,6 +264,13 @@ const formatCase = (applicationContext, caseDetail) => { .formatDateString(document.signedAt, 'DATE_TIME_TZ'), })); + if (result.correspondence && result.correspondence.length) { + result.correspondence.forEach(doc => { + doc.formattedFilingDate = applicationContext + .getUtilities() + .formatDateString(doc.filingDate, 'MMDDYY'); + }); + } // establish an initial sort by ascending index result.docketRecordWithDocument.sort((a, b) => { return a.index - b.index; @@ -309,8 +321,8 @@ const formatCase = (applicationContext, caseDetail) => { result.formattedPreferredTrialCity = result.preferredTrialCity || 'No location selected'; - if (result.trialSessionId && result.status !== Case.STATUS_TYPES.closed) { - if (result.status === Case.STATUS_TYPES.calendared) { + if (result.trialSessionId && result.status !== CASE_STATUS_TYPES.closed) { + if (result.status === CASE_STATUS_TYPES.calendared) { result.showTrialCalendared = true; } else { result.showScheduled = true; @@ -367,12 +379,12 @@ const formatCase = (applicationContext, caseDetail) => { let paymentDate = ''; let paymentMethod = ''; - if (caseDetail.petitionPaymentStatus === Case.PAYMENT_STATUS.PAID) { + if (caseDetail.petitionPaymentStatus === PAYMENT_STATUS.PAID) { paymentDate = applicationContext .getUtilities() .formatDateString(caseDetail.petitionPaymentDate, 'MM/DD/YY'); paymentMethod = caseDetail.petitionPaymentMethod; - } else if (caseDetail.petitionPaymentStatus === Case.PAYMENT_STATUS.WAIVED) { + } else if (caseDetail.petitionPaymentStatus === PAYMENT_STATUS.WAIVED) { paymentDate = applicationContext .getUtilities() .formatDateString(caseDetail.petitionPaymentWaivedDate, 'MM/DD/YY'); diff --git a/shared/src/business/utilities/getFormattedCaseDetail.test.js b/shared/src/business/utilities/getFormattedCaseDetail.test.js index 7c85d3198ba..864cd95f06c 100644 --- a/shared/src/business/utilities/getFormattedCaseDetail.test.js +++ b/shared/src/business/utilities/getFormattedCaseDetail.test.js @@ -1,4 +1,3 @@ -import { Case } from '../entities/cases/Case'; import { TRANSCRIPT_AGE_DAYS_MIN, documentMeetsAgeRequirements, @@ -9,9 +8,13 @@ import { getFormattedCaseDetail, sortDocketRecords, } from './getFormattedCaseDetail'; -import { User } from '../entities/User'; import { applicationContext } from '../../../../web-client/src/applicationContext'; import { calculateISODate, createISODateString } from './DateHandler'; +const { + CASE_STATUS_TYPES, + PAYMENT_STATUS, + ROLES, +} = require('../entities/EntityConstants'); const { MOCK_USERS } = require('../../test/mockUsers'); applicationContext.getCurrentUser = () => @@ -19,6 +22,7 @@ applicationContext.getCurrentUser = () => const mockCaseDetailBase = { caseId: '123-456-abc-def', + correspondence: [], createdAt: new Date(), docketNumber: '123-45', docketNumberSuffix: 'S', @@ -103,6 +107,21 @@ describe('formatCase', () => { expect(result.documents[1].qcWorkItemsUntouched).toEqual(false); }); + it('should format the filing date of all correspondence documents', () => { + const result = formatCase(applicationContext, { + ...mockCaseDetail, + correspondence: [ + { + documentTitle: 'Test Correspondence', + filedBy: 'Test Docket Clerk', + filingDate: '2020-05-21T18:21:59.818Z', + }, + ], + }); + + expect(result.correspondence[0].formattedFilingDate).toEqual('05/21/20'); + }); + it('should format docket records if the case docket record array is set', () => { const result = formatCase(applicationContext, { ...mockCaseDetail, @@ -413,7 +432,7 @@ describe('formatCase', () => { it('should format trial details if case status is calendared', () => { const result = formatCase(applicationContext, { ...mockCaseDetail, - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: '2011-11-11', trialLocation: 'Boise, Idaho', trialSessionId: '1f1aa3f7-e2e3-43e6-885d-4ce341588c76', @@ -453,7 +472,7 @@ describe('formatCase', () => { it('should format trial details with incomplete trial information', () => { const result = formatCase(applicationContext, { ...mockCaseDetail, - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: undefined, trialLocation: undefined, trialSessionId: '1f1aa3f7-e2e3-43e6-885d-4ce341588c76', @@ -478,7 +497,7 @@ describe('formatCase', () => { it('should show not scheduled section if case status is closed', () => { const result = formatCase(applicationContext, { ...mockCaseDetail, - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }); expect(result).toMatchObject({ @@ -595,7 +614,7 @@ describe('formatDocument', () => { it('should set the servedPartiesCode to `R` if servedAt date exists and servedParties is an array of length 1 with role irsSuperuser', () => { const results = formatDocument(applicationContext, { servedAt: '2019-03-27T21:53:00.297Z', - servedParties: [{ role: User.ROLES.irsSuperuser }], + servedParties: [{ role: ROLES.irsSuperuser }], }); expect(results).toMatchObject({ servedPartiesCode: 'R', @@ -737,7 +756,7 @@ it('should format filing fee string for a paid petition fee', () => { ...mockCaseDetailBase, petitionPaymentDate: '2019-03-01T21:40:46.415Z', petitionPaymentMethod: 'check', - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, + petitionPaymentStatus: PAYMENT_STATUS.PAID, }, }); @@ -749,7 +768,7 @@ it('should format filing fee string for a waived petition fee', () => { applicationContext, caseDetail: { ...mockCaseDetailBase, - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, petitionPaymentWaivedDate: '2019-03-01T21:40:46.415Z', }, }); @@ -762,7 +781,7 @@ it('should format filing fee string for an unpaid petition fee', () => { applicationContext, caseDetail: { ...mockCaseDetailBase, - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, }, }); diff --git a/shared/src/business/utilities/getFormattedJudgeName.js b/shared/src/business/utilities/getFormattedJudgeName.js index c1766d041bc..6f2512081ad 100644 --- a/shared/src/business/utilities/getFormattedJudgeName.js +++ b/shared/src/business/utilities/getFormattedJudgeName.js @@ -1,4 +1,8 @@ const formatJudgeName = name => { + if (!name) { + return ''; + } + return name.replace(/^Judge\s+/, ''); }; diff --git a/shared/src/business/utilities/getFormattedJudgeName.test.js b/shared/src/business/utilities/getFormattedJudgeName.test.js index 43d9fac4aa9..d88b093777f 100644 --- a/shared/src/business/utilities/getFormattedJudgeName.test.js +++ b/shared/src/business/utilities/getFormattedJudgeName.test.js @@ -6,6 +6,12 @@ describe('formatJudgeName', () => { expect(result).toMatch('Rummy'); }); + + it('returns an empty string for undefined values', () => { + const result = formatJudgeName(undefined); + + expect(result).toEqual(''); + }); }); describe('getJudgeLastName', () => { diff --git a/shared/src/business/utilities/getFormattedTrialSessionDetails.test.js b/shared/src/business/utilities/getFormattedTrialSessionDetails.test.js index a654802f478..1a2a6e4f7e4 100644 --- a/shared/src/business/utilities/getFormattedTrialSessionDetails.test.js +++ b/shared/src/business/utilities/getFormattedTrialSessionDetails.test.js @@ -1,14 +1,12 @@ import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; -import { TrialSession } from '../entities/trialSessions/TrialSession'; -const { SESSION_STATUS_GROUPS } = TrialSession; +import { SESSION_STATUS_GROUPS } from '../entities/EntityConstants'; +import { applicationContext } from '../../../../web-client/src/applicationContext'; import { formattedTrialSessionDetails, getTrialSessionStatus, } from './getFormattedTrialSessionDetails'; import { omit } from 'lodash'; -import { applicationContext } from '../../../../web-client/src/applicationContext'; - describe('formattedTrialSessionDetails', () => { const TRIAL_SESSION = { caseOrder: [], diff --git a/shared/src/business/utilities/htmlGenerator/index.scss b/shared/src/business/utilities/htmlGenerator/index.scss index 644270620e5..e4af36c3ad4 100644 --- a/shared/src/business/utilities/htmlGenerator/index.scss +++ b/shared/src/business/utilities/htmlGenerator/index.scss @@ -75,13 +75,21 @@ th { } .width-half { - width: 50%; + width: 49%; +} + +.margin-top-40 { + margin-top: 40px; } .margin-top-80 { margin-top: 80px; } +.margin-top-200 { + margin-top: 200px; +} + .margin-top-5 { margin-top: 5px; } @@ -140,6 +148,10 @@ th { font-size: 14px; line-height: 18px; + &.border-none { + border: none; + } + #caption-extension, #caption-v, #caption-respondent { @@ -171,6 +183,24 @@ th { .clear { clear: both; } + +.column { + display: flex; + flex: 1; + + .width-half { + width: 50%; + + &:first-child { + margin-right: 6px; + } + + &:last-child { + margin-left: 6px; + } + } +} + .card { border: 1px solid #ccc; margin: 12px 0 12px 0; @@ -186,7 +216,6 @@ th { .card-content { display: flex; - flex-flow: row wrap; align-items: flex-start; padding: 10px; } @@ -202,7 +231,7 @@ th { } .info-box-header { - padding: 14px; + padding: 8px; border-bottom: 1px solid #ccc; background: #f0f0f0; font-weight: bold; @@ -212,7 +241,7 @@ th { } .info-box-content { - padding: 0 10px 10px 10px; + padding: 10px; } .info-box-trial { @@ -231,7 +260,7 @@ th { .signature { margin: 100px 0 0 70%; font-family: 'nimbus_roman', serif; - font-size: 12px; + font-size: 14px; line-height: 13px; } @@ -250,12 +279,12 @@ th { } @page { - margin-top: 100px; - margin-bottom: 55px; + margin: 2cm 1cm; + size: 8.5in 11in; } - @page :first { - margin-top: 0; + margin-top: 1cm; + margin-bottom: 2cm; } .margin-top-0 { @@ -333,6 +362,20 @@ th { text-decoration: none; } } +#document-cover-sheet { + .case-information, + #docket-number, + #document-title, + #certificate-of-service { + font-family: 'nimbus_sans_l', sans-serif !important; + } + + #certificate-of-service { + position: absolute; + bottom: 60px; + left: 60; + } +} #document-docket-record { // TODO: These are basically cards, and could be refactored for more reusability @@ -373,6 +416,31 @@ th { } } +.calendar-icon { + width: 12px; + height: 12px; + background: url('data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhciIgZGF0YS1pY29uPSJjYWxlbmRhci10aW1lcyIgY2xhc3M9InN2Zy1pbmxpbmUtLWZhIGZhLWNhbGVuZGFyLXRpbWVzIGZhLXctMTQiIHJvbGU9ImltZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNDQ4IDUxMiI+PHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMzExLjcgMzc0LjdsLTE3IDE3Yy00LjcgNC43LTEyLjMgNC43LTE3IDBMMjI0IDMzNy45bC01My43IDUzLjdjLTQuNyA0LjctMTIuMyA0LjctMTcgMGwtMTctMTdjLTQuNy00LjctNC43LTEyLjMgMC0xN2w1My43LTUzLjctNTMuNy01My43Yy00LjctNC43LTQuNy0xMi4zIDAtMTdsMTctMTdjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDUzLjcgNTMuNyA1My43LTUzLjdjNC43LTQuNyAxMi4zLTQuNyAxNyAwbDE3IDE3YzQuNyA0LjcgNC43IDEyLjMgMCAxN0wyNTcuOSAzMDRsNTMuNyA1My43YzQuOCA0LjcgNC44IDEyLjMuMSAxN3pNNDQ4IDExMnYzNTJjMCAyNi41LTIxLjUgNDgtNDggNDhINDhjLTI2LjUgMC00OC0yMS41LTQ4LTQ4VjExMmMwLTI2LjUgMjEuNS00OCA0OC00OGg0OFYxMmMwLTYuNiA1LjQtMTIgMTItMTJoNDBjNi42IDAgMTIgNS40IDEyIDEydjUyaDEyOFYxMmMwLTYuNiA1LjQtMTIgMTItMTJoNDBjNi42IDAgMTIgNS40IDEyIDEydjUyaDQ4YzI2LjUgMCA0OCAyMS41IDQ4IDQ4em0tNDggMzQ2VjE2MEg0OHYyOThjMCAzLjMgMi43IDYgNiA2aDM0MGMzLjMgMCA2LTIuNyA2LTZ6Ij48L3BhdGg+PC9zdmc+'); + background-size: 12px 12px; +} + +#address-label-cover-sheet { + font-family: sans-serif; + font-size: 12px; + + .docket { + position: absolute; + top: 0; + left: 0; + padding-left: 0.6in; + } + + .address-label { + position: absolute; + bottom: 0in; + left: 0.5in; + } +} + @font-face { font-family: 'nimbus_roman'; font-style: normal; diff --git a/shared/src/business/utilities/pdfGenerator/components/AddressLabel.jsx b/shared/src/business/utilities/pdfGenerator/components/AddressLabel.jsx new file mode 100644 index 00000000000..fa5cca7d626 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/components/AddressLabel.jsx @@ -0,0 +1,25 @@ +const React = require('react'); + +export const AddressLabel = ({ + address1, + address2, + address3, + city, + countryName, + name, + postalCode, + state, +}) => { + return ( +
+
{name}
+
{address1}
+ {address2 &&
{address2}
} + {address3 &&
{address3}
} +
+ {city}, {state} {postalCode} +
+ {!address3 &&
{countryName}
} +
+ ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/components/AddressLabel.test.js b/shared/src/business/utilities/pdfGenerator/components/AddressLabel.test.js new file mode 100644 index 00000000000..52ed2c38f82 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/components/AddressLabel.test.js @@ -0,0 +1,43 @@ +const React = require('react'); +const { AddressLabel } = require('./AddressLabel.jsx'); +const { shallow } = require('enzyme'); + +describe('AddressLabel', () => { + it('renders the name and address', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.text()).toContain('123 Some Street'); + expect(wrapper.text()).toContain('Some City'); + expect(wrapper.text()).toContain('USA'); + expect(wrapper.text()).toContain('Test Person'); + expect(wrapper.text()).toContain('89890'); + expect(wrapper.text()).toContain('ZZ'); + }); + + it('renders optional address information if present', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.text()).toContain('address two'); + expect(wrapper.text()).toContain('address three'); + expect(wrapper.text()).not.toContain('USA'); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/components/DatePrintedFooter.test.js b/shared/src/business/utilities/pdfGenerator/components/DatePrintedFooter.test.js new file mode 100644 index 00000000000..d2ed537a5b2 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/components/DatePrintedFooter.test.js @@ -0,0 +1,10 @@ +const React = require('react'); +const { DatePrintedFooter } = require('./DatePrintedFooter.jsx'); +const { shallow } = require('enzyme'); + +describe('DatePrintedFooter', () => { + it('renders the given dateServed from props', () => { + let wrapper = shallow(); + expect(wrapper.text()).toContain('Printed 01/01/2020'); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/components/DateServedFooter.jsx b/shared/src/business/utilities/pdfGenerator/components/DateServedFooter.jsx new file mode 100644 index 00000000000..aed02ef3e1e --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/components/DateServedFooter.jsx @@ -0,0 +1,18 @@ +const React = require('react'); + +export const DateServedFooter = ({ dateServed }) => { + return ( +
+ SERVED {dateServed} +
+ ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/components/DateServedFooter.test.js b/shared/src/business/utilities/pdfGenerator/components/DateServedFooter.test.js new file mode 100644 index 00000000000..087b3169643 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/components/DateServedFooter.test.js @@ -0,0 +1,10 @@ +const React = require('react'); +const { DateServedFooter } = require('./DateServedFooter.jsx'); +const { shallow } = require('enzyme'); + +describe('DateServedFooter', () => { + it('renders the given dateServed from props', () => { + let wrapper = shallow(); + expect(wrapper.text()).toContain('SERVED 01/01/2020'); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/components/PageMetaHeaderDocket.jsx b/shared/src/business/utilities/pdfGenerator/components/PageMetaHeaderDocket.jsx index 10320cb10cf..39b428c99e7 100644 --- a/shared/src/business/utilities/pdfGenerator/components/PageMetaHeaderDocket.jsx +++ b/shared/src/business/utilities/pdfGenerator/components/PageMetaHeaderDocket.jsx @@ -3,10 +3,14 @@ const React = require('react'); export const PageMetaHeaderDocket = ({ docketNumber }) => { return ( <> -
+
Docket Number: {docketNumber}
-
+
Page of{' '}
diff --git a/shared/src/business/utilities/pdfGenerator/components/ReportsMetaHeader.jsx b/shared/src/business/utilities/pdfGenerator/components/ReportsMetaHeader.jsx index 99e4b75bc14..c3350a20c6c 100644 --- a/shared/src/business/utilities/pdfGenerator/components/ReportsMetaHeader.jsx +++ b/shared/src/business/utilities/pdfGenerator/components/ReportsMetaHeader.jsx @@ -3,10 +3,15 @@ const React = require('react'); export const ReportsMetaHeader = ({ headerTitle }) => { return ( <> -
+
{headerTitle}
-
+
Page of{' '}
diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/AddressLabelCoverSheet.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/AddressLabelCoverSheet.jsx new file mode 100644 index 00000000000..588e6ea9499 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/AddressLabelCoverSheet.jsx @@ -0,0 +1,30 @@ +const React = require('react'); +const { AddressLabel } = require('../components/AddressLabel.jsx'); + +export const AddressLabelCoverSheet = ({ + address1, + address2, + address3, + city, + countryName, + docketNumberWithSuffix, + name, + postalCode, + state, +}) => { + return ( +
+
Docket {docketNumberWithSuffix}
+ +
+ ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/AddressLabelCoverSheet.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/AddressLabelCoverSheet.test.js new file mode 100644 index 00000000000..bd01cb57cd9 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/AddressLabelCoverSheet.test.js @@ -0,0 +1,34 @@ +const React = require('react'); +const { AddressLabelCoverSheet } = require('./AddressLabelCoverSheet.jsx'); +const { mount } = require('enzyme'); + +describe('AddressLabelCoverSheet', () => { + it('renders the docket number with suffix', () => { + const wrapper = mount( + , + ); + + expect(wrapper.text()).toContain('Docket 123-45S'); + }); + + it('renders the name and address', () => { + const wrapper = mount( + , + ); + + expect(wrapper.text()).toContain('123 Some Street'); + expect(wrapper.text()).toContain('Some City'); + expect(wrapper.text()).toContain('USA'); + expect(wrapper.text()).toContain('Test Person'); + expect(wrapper.text()).toContain('89890'); + expect(wrapper.text()).toContain('ZZ'); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/CoverSheet.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/CoverSheet.jsx new file mode 100644 index 00000000000..e48ef6edced --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/CoverSheet.jsx @@ -0,0 +1,66 @@ +const React = require('react'); + +export const CoverSheet = ({ + caseCaptionExtension, + caseTitle, + certificateOfService, + dateFiledLodged, + dateFiledLodgedLabel, + dateReceived, + docketNumberWithSuffix, + documentTitle, + electronicallyFiled, + mailingDate, +}) => { + return ( +
+
+
+
+
+
+
+ Received +
+ {dateReceived} +
+
+
+
+ {dateFiledLodgedLabel} +
+ {dateFiledLodged} +
+
+
+ +
+
+
{caseTitle}
+
{caseCaptionExtension}
+
v.
+
Commissioner of Internal Revenue
+
Respondent
+
+ +
+ {electronicallyFiled &&
Electronically Filed
} + {mailingDate &&
{mailingDate}
} +
Docket Number {docketNumberWithSuffix}
+
+
+
+ +

+ {documentTitle} +

+ + {certificateOfService && ( +
Certificate of Service
+ )} +
+ ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/CoverSheet.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/CoverSheet.test.js new file mode 100644 index 00000000000..3e3e883f676 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/CoverSheet.test.js @@ -0,0 +1,76 @@ +const React = require('react'); +const { CoverSheet } = require('./CoverSheet.jsx'); +const { shallow } = require('enzyme'); + +describe('CoverSheet', () => { + it('renders a document header with case information', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.find('#caption-title').text()).toEqual('Captain Fantastic'); + expect(wrapper.find('#caption-extension').text()).toEqual('Petitioner'); + expect(wrapper.find('#docket-number').text()).toContain( + 'Docket Number 123-45S', + ); + }); + + it('renders the received date', () => { + const wrapper = shallow(); + const text = wrapper.find('#date-received').text(); + + expect(text).toContain('Received'); + expect(text).toContain('01/01/2020'); + }); + + it('renders a filed or lodged label along with the associated date', () => { + const wrapper = shallow( + , + ); + const text = wrapper.find('#filed-or-lodged').text(); + + expect(text).toContain('Some Label'); + expect(text).toContain('02/02/2020'); + }); + + it('renders Electronically Filed if the case was filed electronically', () => { + let wrapper = shallow(); + + expect(wrapper.find('#docket-number').text()).not.toContain( + 'Electronically Filed', + ); + + wrapper = shallow(); + + expect(wrapper.find('#docket-number').text()).toContain( + 'Electronically Filed', + ); + }); + + it('renders the mailingDate if provided', () => { + let wrapper = shallow(); + + expect(wrapper.find('#docket-number').text()).not.toContain('12/1/2019'); + + wrapper = shallow(); + + expect(wrapper.find('#docket-number').text()).toContain('12/1/2019'); + }); + + it('renders Certificate of Service of there is a certificateOfService', () => { + let wrapper = shallow(); + + expect(wrapper.find('#certificate-of-service').length).toEqual(0); + + wrapper = shallow(); + + expect(wrapper.find('#certificate-of-service').length).toEqual(1); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/DocketRecord.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/DocketRecord.test.js index 1db0df5652d..2412e390e3d 100644 --- a/shared/src/business/utilities/pdfGenerator/documentTemplates/DocketRecord.test.js +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/DocketRecord.test.js @@ -1,5 +1,5 @@ const React = require('react'); -const { ContactFactory } = require('../../../entities/contacts/ContactFactory'); +const { COUNTRY_TYPES } = require('../../../entities/EntityConstants'); const { DocketRecord } = require('./DocketRecord.jsx'); const { mount } = require('enzyme'); @@ -107,7 +107,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -138,7 +138,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -158,7 +158,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -181,13 +181,13 @@ describe('DocketRecord', () => { }); it("displays a party's country if international", () => { - contactPrimary.countryType = ContactFactory.COUNTRY_TYPES.INTERNATIONAL; + contactPrimary.countryType = COUNTRY_TYPES.INTERNATIONAL; contactPrimary.country = 'The Republic of Texas'; const wrapper = mount( , @@ -205,7 +205,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -225,7 +225,7 @@ describe('DocketRecord', () => { let wrapper = mount( , @@ -238,7 +238,7 @@ describe('DocketRecord', () => { wrapper = mount( , @@ -279,7 +279,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -308,7 +308,7 @@ describe('DocketRecord', () => { let wrapper = mount( , @@ -321,7 +321,7 @@ describe('DocketRecord', () => { wrapper = mount( , @@ -350,7 +350,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -376,7 +376,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -394,7 +394,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -411,7 +411,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -428,7 +428,7 @@ describe('DocketRecord', () => { const wrapper = mount( , @@ -446,7 +446,7 @@ describe('DocketRecord', () => { const wrapper = mount( , diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/NoticeOfReceiptOfPetition.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/NoticeOfReceiptOfPetition.jsx new file mode 100644 index 00000000000..518368afa48 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/NoticeOfReceiptOfPetition.jsx @@ -0,0 +1,96 @@ +const React = require('react'); +const { + CompressedDocketHeader, +} = require('../components/CompressedDocketHeader.jsx'); +const { AddressLabel } = require('../components/AddressLabel.jsx'); +const { PrimaryHeader } = require('../components/PrimaryHeader.jsx'); + +export const NoticeOfReceiptOfPetition = ({ + address, + caseCaptionExtension, + caseTitle, + docketNumberWithSuffix, + preferredTrialCity, + receivedAtFormatted, + servedDate, +}) => { + return ( + <> + + +
+ The Court received and filed your petition on {receivedAtFormatted} and + served it on respondent on {servedDate}. +
+ +
+ (X) Request for Place of Trial at {preferredTrialCity}. +
+ +
+
+ Your Docket Number: {docketNumberWithSuffix} +
+
+ Please use this docket number on all papers and correspondence that + you send to the Tax Court. Do not include your Social Security or + Taxpayer Identification numbers on any documents you file with the + Court. +
+
+ +
+
Internet Access:
+
+ To obtain further information about proceeding in the Tax Court, + please visit{' '} + + www.ustaxcourt.gov + {' '} + and select "Taxpayer Identification". +
+
+ +
+
Change of Address:
+
+ You must notify the Clerk of the Court if you change your address. If + you filed your petition in paper, see Tax Court Form 10, Notice of + Change of Address, under “Forms” on the Tax Court’s Website at{' '} + + www.ustaxcourt.gov + + . If you filed your petition electronically, you may update your + address under the “Case Information” tab in your case online. Failure + to notify the Clerk of the Court of a change of your address can mean + you do not receive notices and documents essential to your case and + can lead to dismissal of your case. +
+
+ +

+ Stephanie A. Servoss +
+ Clerk of the Court +

+ +
+ +
+ + ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/NoticeOfReceiptOfPetition.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/NoticeOfReceiptOfPetition.test.js new file mode 100644 index 00000000000..962b368070c --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/NoticeOfReceiptOfPetition.test.js @@ -0,0 +1,78 @@ +const React = require('react'); +const { + NoticeOfReceiptOfPetition, +} = require('./NoticeOfReceiptOfPetition.jsx'); +const { mount, shallow } = require('enzyme'); + +describe('NoticeOfReceiptOfPetition', () => { + const caseCaptionExtension = 'Petitioner(s)'; + const caseTitle = 'Test Petitioner'; + const docketNumberWithSuffix = '123-19S'; + const address = { + address1: '123 Some St.', + city: 'Somecity', + countryName: '', + name: 'Test Petitioner', + postalCode: '80008', + state: 'ZZ', + }; + + it('renders a document header with case information', () => { + const wrapper = mount( + , + ); + + expect(wrapper.find('#caption').text()).toContain(caseTitle); + expect(wrapper.find('#caption').text()).toContain(caseCaptionExtension); + expect(wrapper.find('#docket-number').text()).toEqual( + `Docket Number ${docketNumberWithSuffix}`, + ); + expect(wrapper.find('h3').at(0).text()).toEqual( + 'Notice of Receipt of Petition', + ); + }); + + it('renders the case information', () => { + const wrapper = shallow( + , + ); + const textContent = wrapper.text(); + expect(textContent).toContain(docketNumberWithSuffix); + expect(textContent).toContain('Birmingham, AL'); + expect(textContent).toContain('December 1, 2019'); + expect(textContent).toContain('June 3, 2020'); + }); + + it('renders the the petitioner mailing address', () => { + const wrapper = mount( + , + ); + const textContent = wrapper.text(); + expect(textContent).toContain(address.name); + expect(textContent).toContain(address.address1); + expect(textContent).toContain(address.city); + expect(textContent).toContain(address.state); + expect(textContent).toContain(address.postalCode); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/Order.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/Order.jsx new file mode 100644 index 00000000000..7af0645c675 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/Order.jsx @@ -0,0 +1,31 @@ +const React = require('react'); + +const { DocketHeader } = require('../components/DocketHeader.jsx'); +const { PrimaryHeader } = require('../components/PrimaryHeader.jsx'); + +export const Order = ({ options, orderContent, orderTitle, signatureText }) => { + return ( + <> + + +
+ {signatureText && ( +
+

+ {signatureText} +
+ Clerk of the Court +

+
+ )} + + ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/Order.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/Order.test.js new file mode 100644 index 00000000000..ce3e5aa4ab7 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/Order.test.js @@ -0,0 +1,74 @@ +const React = require('react'); +const { mount } = require('enzyme'); +const { Order } = require('./Order.jsx'); + +describe('Order', () => { + let options; + let orderContent; + let orderTitle; + + beforeAll(() => { + options = { + caseCaptionExtension: 'Petitioner(s)', + caseTitle: 'Test Petitioner', + docketNumberWithSuffix: '123-45S', + }; + + orderContent = '

This is some order content

'; + orderTitle = 'Test Order Title'; + }); + + it('renders a document header with case information', () => { + const wrapper = mount(); + + expect(wrapper.find('#caption-title').text()).toEqual(options.caseTitle); + expect(wrapper.find('#caption-extension').text()).toEqual( + options.caseCaptionExtension, + ); + expect(wrapper.find('#docket-number').text()).toEqual( + `Docket Number ${options.docketNumberWithSuffix}`, + ); + }); + + it('renders a document title', () => { + const wrapper = mount(); + + expect(wrapper.find('.case-information h3').text()).toEqual(orderTitle); + }); + + it('renders the order content', () => { + const wrapper = mount( + , + ); + + expect(wrapper.find('#order-content').text()).toEqual( + 'This is some order content', + ); + }); + + it('renders a signature if provided', () => { + let wrapper = mount( + , + ); + expect(wrapper.find('#order-signature').length).toEqual(0); + + wrapper = mount( + , + ); + expect(wrapper.find('#order-signature').text()).toContain('Test Signature'); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.jsx index 6d37ebd70a7..54fec21e74e 100644 --- a/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.jsx +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.jsx @@ -25,11 +25,11 @@ export const PendingReport = ({ pendingItems, subtitle }) => { return ( {pendingItem.docketNumberWithSuffix} - {pendingItem.dateFiled} + {pendingItem.formattedFiledDate} {pendingItem.caseTitle} - {pendingItem.filingsAndProceedings} + {pendingItem.formattedName} {pendingItem.status} - {pendingItem.judge} + {pendingItem.associatedJudgeFormatted} ); })} diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.test.js index 7bb2014dc4c..f4d775680d2 100644 --- a/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.test.js +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/PendingReport.test.js @@ -8,11 +8,11 @@ describe('PendingReport', () => { beforeAll(() => { pendingItems = [ { + associatedJudgeFormatted: 'Chief Judge', caseTitle: 'Test Petitioner', - dateFiled: '02/02/20', docketNumberWithSuffix: '123-45S', - filingsAndProceedings: 'Order', - judge: 'Chief Judge', + formattedFiledDate: '02/02/20', + formattedName: 'Order', status: 'closed', }, ]; @@ -36,15 +36,17 @@ describe('PendingReport', () => { pendingItems[0].docketNumberWithSuffix, ); expect(tableData.find('tr').at(0).text()).toContain( - pendingItems[0].dateFiled, + pendingItems[0].formattedFiledDate, ); expect(tableData.find('tr').at(0).text()).toContain( pendingItems[0].caseTitle, ); expect(tableData.find('tr').at(0).text()).toContain( - pendingItems[0].filingsAndProceedings, + pendingItems[0].formattedName, ); expect(tableData.find('tr').at(0).text()).toContain(pendingItems[0].status); - expect(tableData.find('tr').at(0).text()).toContain(pendingItems[0].judge); + expect(tableData.find('tr').at(0).text()).toContain( + pendingItems[0].associatedJudgeFormatted, + ); }); }); diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/StandingPretrialNotice.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/StandingPretrialNotice.jsx new file mode 100644 index 00000000000..887af65fce0 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/StandingPretrialNotice.jsx @@ -0,0 +1,127 @@ +const React = require('react'); + +const { DocketHeader } = require('../components/DocketHeader.jsx'); +const { PrimaryHeader } = require('../components/PrimaryHeader.jsx'); + +export const StandingPretrialNotice = ({ footerDate, options, trialInfo }) => { + return ( + <> + + + +
+
+ This case is set for the trial session beginning at{' '} + {trialInfo.startTime} on{' '} + {trialInfo.fullStartDate}. +
+
+
{trialInfo.courthouseName}
+
{trialInfo.address1}
+ {trialInfo.address2 && ( +
{trialInfo.address2}
+ )} + {trialInfo.address3 && ( +
{trialInfo.address3}
+ )} +
+ {trialInfo.city}, {trialInfo.state} {trialInfo.postalCode} +
+
+
+ +
    +
  • + WHAT TO DO NOW +
      +
    • + Call respondent’s counsel (the IRS lawyer) as soon as possible. + Their name and phone number is {trialInfo.respondentContactText}. +
        +
      • Tell them if English is not your first language.
      • +
      • + Send them copies of documents that you think help your case. +
      • +
      • + Find out whether you will need to go to trial or if you can + settle your case. +
      • +
      +
    • +
    +
  • +
  • + WHAT TO DO BEFORE TRIAL +
      +
    • + Talk to the IRS lawyer about what facts and documents you can both + stipulate (agree) to. Facts might include your name, address, and + the tax year(s) involved. Documents might include the notice or + your tax returns. +
    • +
    • + For documents you cannot agree on, bring 3 copies to trial. A + photocopier may not be available at the courthouse. Only documents + presented at trial will be part of the case record. The Tax Court + is not part of the IRS. If you gave something to the IRS before, + bring a copy with you! +
    • +
    • + Consider filing a "pretrial memorandum" because it is + helpful to the Judge. A form you can use is attached. +
    • +
    • + You can send an update to the Court a week in advance if you need + to by filing a Final Status Report, available at + https://www.ustaxcourt.gov/FinalStatusReport/. +
    • +
    • + Your case may not be tried on {trialInfo.fullStartDate} because + trial sessions can last more than one day. If you have witnesses + that can only be there on {trialInfo.startDay}, please put that in + your pretrial memorandum or Final Status Report. +
    • +
    +
  • +
  • + WHAT TO DO THE DAY OF THE TRIAL SESSION +
      +
    • + Arrive 1 hour early because you will have to go through security. + Bring a government-issued photo ID. +
    • +
    • Don’t forget copies of your documents.
    • +
    • + Be prepared for trial. Cases will not be continued (postponed) + other than in unusual situations. +
    • +
    +
  • +
  • + Find out more at{' '} + www.ustaxcourt.gov or call + call 202-521-0700. +
  • +
+
+

+ (Signed) {trialInfo.judge.name} +
+ Trial Judge +

+
+

+ Served {footerDate} +

+ + ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/StandingPretrialNotice.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/StandingPretrialNotice.test.js new file mode 100644 index 00000000000..d17eb777999 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/StandingPretrialNotice.test.js @@ -0,0 +1,130 @@ +const React = require('react'); +const { mount, shallow } = require('enzyme'); +import { StandingPretrialNotice } from './StandingPretrialNotice.jsx'; + +describe('StandingPretrialNotice', () => { + let options; + let trialInfo; + let footerDate = '05/01/2020'; + + beforeAll(() => { + options = { + caseCaptionExtension: 'Petitioner(s)', + caseTitle: 'Test Petitioner', + docketNumberWithSuffix: '123-45S', + }; + + trialInfo = { + address1: '123 Some St.', + city: 'Some City', + courthouseName: 'Hall of Justice', + fullStartDate: 'Friday May 8, 2020', + judge: { + name: 'Test Judge', + }, + postalCode: '12345', + startDay: 'Friday', + startTime: '10:00am', + state: 'TEST STATE', + }; + }); + + it('renders a document header with case information', () => { + const wrapper = mount( + , + ); + + expect(wrapper.find('#caption-title').text()).toEqual(options.caseTitle); + expect(wrapper.find('#caption-extension').text()).toEqual( + options.caseCaptionExtension, + ); + expect(wrapper.find('#docket-number').text()).toEqual( + `Docket Number ${options.docketNumberWithSuffix}`, + ); + }); + + it('renders the trial start date and time', () => { + const wrapper = shallow( + , + ); + + const trialInformation = wrapper.find('#trial-information'); + + expect(trialInformation.text()).toContain(trialInfo.address1); + expect(trialInformation.text()).toContain(trialInfo.city); + expect(trialInformation.text()).toContain(trialInfo.state); + expect(trialInformation.text()).toContain(trialInfo.postalCode); + }); + + it('renders the trial location information', () => { + const wrapper = shallow( + , + ); + + const trialInformation = wrapper.find('#trial-information'); + + expect(trialInformation.text()).toContain(trialInfo.courthouseName); + expect(trialInformation.text()).toContain(trialInfo.address1); + expect(trialInformation.text()).toContain(trialInfo.city); + expect(trialInformation.text()).toContain(trialInfo.state); + expect(trialInformation.text()).toContain(trialInfo.postalCode); + }); + + it('renders the trial location information', () => { + const wrapper = shallow( + , + ); + + const trialLocation = wrapper.find('#trial-location'); + + expect(trialLocation.text()).toContain(trialInfo.address1); + expect(trialLocation.text()).toContain(trialInfo.city); + expect(trialLocation.text()).toContain(trialInfo.state); + expect(trialLocation.text()).toContain(trialInfo.postalCode); + + const optionalAddress = wrapper.find('.address-optional'); + expect(optionalAddress.length).toEqual(0); + }); + + it('renders optional trial location information if present', () => { + const wrapper = shallow( + , + ); + + const trialLocation = wrapper.find('#trial-location'); + + expect(trialLocation.text()).toContain('Address Two'); + expect(trialLocation.text()).toContain('Address Three'); + }); + + it('renders the trial judge signature', () => { + const wrapper = shallow( + , + ); + + const signature = wrapper.find('.signature'); + + expect(signature.text()).toContain(`(Signed) ${trialInfo.judge.name}`); + }); + + it('renders the served date', () => { + const wrapper = shallow( + , + ); + + const servedStamp = wrapper.find('#served-stamp'); + + expect(servedStamp.text()).toContain(`Served ${footerDate}`); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialCalendar.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialCalendar.jsx new file mode 100644 index 00000000000..1aa03d063d8 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialCalendar.jsx @@ -0,0 +1,122 @@ +const React = require('react'); + +const { PrimaryHeader } = require('../components/PrimaryHeader.jsx'); +const { ReportsHeader } = require('../components/ReportsHeader.jsx'); + +export const TrialCalendar = ({ cases, sessionDetail }) => { + return ( + <> + + + +
+
+
Trial Information
+
+
+ Start Time +
+ {sessionDetail.startTime} +
+
+ Location + {sessionDetail.noLocationEntered && ( +
No location entered
+ )} + {!sessionDetail.noLocationEntered && ( + <> + {sessionDetail.courthouseName && ( +
{sessionDetail.courthouseName}
+ )} + {sessionDetail.address1 && ( +
{sessionDetail.address1}
+ )} + {sessionDetail.address2 && ( +
{sessionDetail.address2}
+ )} + {sessionDetail.formattedCityStateZip && ( +
{sessionDetail.formattedCityStateZip}
+ )} + + )} +
+
+
+
+ +
+
Assignments
+
+
+ Judge +
{sessionDetail.judge}
+
+
+ Trial Clerk +
{sessionDetail.trialClerk}
+
+
+
+
+
+ Court Reporter +
{sessionDetail.courtReporter}
+
+
+ IRS Calendar Admin +
{sessionDetail.irsCalendarAdministrator}
+
+
+
+
+
+
+ +
+
Session Notes
+
{sessionDetail.notes}
+
+ +

+ Open Cases ({cases.length}) +

+ + + + + + + + + + + + {cases && + cases.map(caseDetail => { + return ( + + + + + + + ); + })} + +
Docket no.Case TitlePetitioner counselRespondent counsel
{caseDetail.docketNumber}{caseDetail.caseTitle} + {caseDetail.petitionerCounsel && + caseDetail.petitionerCounsel.map((counsel, idx) => ( +
{counsel}
+ ))} +
+ {caseDetail.respondentCounsel && + caseDetail.respondentCounsel.map((counsel, idx) => ( +
{counsel}
+ ))} +
+ + ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialCalendar.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialCalendar.test.js new file mode 100644 index 00000000000..34299eee500 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialCalendar.test.js @@ -0,0 +1,127 @@ +const React = require('react'); +const { mount, shallow } = require('enzyme'); +const { TrialCalendar } = require('./TrialCalendar.jsx'); + +describe('TrialCalendar', () => { + let cases; + let sessionDetail; + + beforeAll(() => { + cases = [ + { + caseTitle: 'Test Petitioner', + docketNumber: '123-45S', + petitionerCounsel: ['Ben Matlock', 'Atticus Finch'], + respondentCounsel: ['Sonny Crockett', 'Ricardo Tubbs'], + }, + ]; + + sessionDetail = { + address1: '123 Some Street', + address2: 'Suite B', + courtReporter: 'Lois Lane', + courthouseName: 'Test Courthouse', + formattedCityStateZip: 'New York, NY 10108', + irsCalendarAdministrator: 'iCalRS Admin', + judge: 'Joseph Dredd', + notes: 'The one with the velour shirt is definitely looking at me funny.', + sessionType: 'Hybrid', + startDate: 'May 1, 2020', + startTime: '10:00am', + trialClerk: 'Clerky McGee', + trialLocation: 'New York City, New York', + }; + }); + + it('renders a document header with the session start date and type concatenated', () => { + const wrapper = mount( + , + ); + + expect(wrapper.find('#reports-header h2').text()).toEqual( + 'New York City, New York', + ); + expect(wrapper.find('#reports-header h3').text()).toEqual( + 'May 1, 2020 Hybrid', + ); + }); + + it('renders trial information', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.find('#start-time').text()).toContain('10:00am'); + expect(wrapper.find('#location').text()).toContain('Test Courthouse'); + expect(wrapper.find('#location').text()).toContain('123 Some Street'); + expect(wrapper.find('#location').text()).toContain('Suite B'); + expect(wrapper.find('#location').text()).toContain('New York, NY 10108'); + }); + + it('does not render location information if it is not entered', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.find('#location').text()).toContain('No location entered'); + + expect(wrapper.find('#location').text()).not.toContain('Test Courthouse'); + expect(wrapper.find('#location').text()).not.toContain('123 Some Street'); + expect(wrapper.find('#location').text()).not.toContain('Suite B'); + expect(wrapper.find('#location').text()).not.toContain( + 'New York, NY 10108', + ); + }); + + it('renders assignment information', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.find('#assignments').text()).toContain('Joseph Dredd'); + expect(wrapper.find('#assignments').text()).toContain('Clerky McGee'); + expect(wrapper.find('#assignments').text()).toContain('Lois Lane'); + expect(wrapper.find('#assignments').text()).toContain('iCalRS Admin'); + }); + + it('renders session notes', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.find('#notes').text()).toContain( + 'The one with the velour shirt is definitely looking at me funny.', + ); + }); + + it('renders the number of open cases', () => { + const wrapper = shallow( + , + ); + + expect(wrapper.find('#cases-count').text()).toEqual( + `Open Cases (${cases.length})`, + ); + }); + + it('renders a table with session case info', () => { + const wrapper = mount( + , + ); + + const tableData = wrapper.find('table tbody'); + + expect(tableData.find('tr').at(0).text()).toContain('123-45S'); + expect(tableData.find('tr').at(0).text()).toContain('Test Petitioner'); + expect(tableData.find('tr').at(0).text()).toContain('Ben Matlock'); + expect(tableData.find('tr').at(0).text()).toContain('Atticus Finch'); + expect(tableData.find('tr').at(0).text()).toContain('Sonny Crockett'); + expect(tableData.find('tr').at(0).text()).toContain('Ricardo Tubbs'); + }); +}); diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialSessionPlanningReport.jsx b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialSessionPlanningReport.jsx new file mode 100644 index 00000000000..cde8a085e06 --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialSessionPlanningReport.jsx @@ -0,0 +1,68 @@ +const React = require('react'); + +const { PrimaryHeader } = require('../components/PrimaryHeader.jsx'); +const { ReportsHeader } = require('../components/ReportsHeader.jsx'); + +const getTermHeaders = (termData, idx) => { + return ( + + {termData.name} {termData.year} + + ); +}; + +const parseTermData = data => + data && data.map((datum, idx) =>
{datum}
); + +const getLocationData = parentIndex => (termData, idx) => { + const hasData = Array.isArray(termData) && termData.length > 0; + + return ( + + {hasData && parseTermData(termData)} + {!hasData &&
} + + ); +}; + +export const TrialSessionPlanningReport = ({ + locationData, + previousTerms, + term, +}) => { + return ( + <> + + + + + + + + + + + + {previousTerms.map(getTermHeaders)} + + + + {locationData && + locationData.map((location, idx) => { + return ( + + + + + + + {location.previousTermsData && + location.previousTermsData.map(getLocationData(idx))} + + ); + })} + +
StateLocationAllSmallRegular
{location.stateAbbreviation}{location.trialCityState}{location.allCaseCount}{location.smallCaseCount}{location.regularCaseCount}
+ + ); +}; diff --git a/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialSessionPlanningReport.test.js b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialSessionPlanningReport.test.js new file mode 100644 index 00000000000..4a138aa037e --- /dev/null +++ b/shared/src/business/utilities/pdfGenerator/documentTemplates/TrialSessionPlanningReport.test.js @@ -0,0 +1,104 @@ +const React = require('react'); +const { + TrialSessionPlanningReport, +} = require('./TrialSessionPlanningReport.jsx'); +const { mount } = require('enzyme'); + +describe('TrialSessionPlanningReport', () => { + const previousTerms = [ + { + name: 'Fall', + year: '2019', + }, + { + name: 'Spring', + year: '2019', + }, + { + name: 'Winter', + year: '2019', + }, + ]; + + const locationData = [ + { + allCaseCount: 5, + previousTermsData: [['(S) Buch', '(R) Cohen'], [], []], + regularCaseCount: 3, + smallCaseCount: 2, + stateAbbreviation: 'AR', + trialCityState: 'Little Rock, AR', + }, + { + allCaseCount: 2, + previousTermsData: [[], [], []], + regularCaseCount: 1, + smallCaseCount: 1, + stateAbbreviation: 'AL', + trialCityState: 'Mobile, AL', + }, + ]; + + it('renders a document header with the report term', () => { + const wrapper = mount( + , + ); + + expect(wrapper.find('#reports-header h2').text()).toEqual( + 'Trial Session Planning Report', + ); + expect(wrapper.find('#reports-header h3').text()).toEqual('Winter 2020'); + }); + + it('creates table headings for the previous terms', () => { + const wrapper = mount( + , + ); + + const tableHeaders = wrapper.find('table thead tr'); + + expect(tableHeaders.text()).toContain('Fall 2019'); + expect(tableHeaders.text()).toContain('Spring 2019'); + expect(tableHeaders.text()).toContain('Winter 2019'); + }); + + it('renders the given location data for the given terms', () => { + const wrapper = mount( + , + ); + + const table = wrapper.find('table tbody tr'); + + expect(table.at(0).text()).toContain('Little Rock, AR'); + expect(table.at(1).text()).toContain('Mobile, AL'); + }); + + it('renders location term data or a calendar icon', () => { + const wrapper = mount( + , + ); + + const tableRow = wrapper.find('table tbody tr'); + + expect(tableRow.at(0).text()).toContain('(S) Buch'); + expect(tableRow.at(0).text()).toContain('(R) Cohen'); + + expect(tableRow.at(1).find('div.calendar-icon').length).toEqual(3); + }); +}); diff --git a/shared/src/business/utilities/setServiceIndicatorsForCase.js b/shared/src/business/utilities/setServiceIndicatorsForCase.js index 87e4e1bff2d..e953e3bc26a 100644 --- a/shared/src/business/utilities/setServiceIndicatorsForCase.js +++ b/shared/src/business/utilities/setServiceIndicatorsForCase.js @@ -1,5 +1,5 @@ const { isEmpty } = require('lodash'); -const { SERVICE_INDICATOR_TYPES } = require('../entities/cases/CaseConstants'); +const { SERVICE_INDICATOR_TYPES } = require('../entities/EntityConstants'); /** * sets the service indicators for parties on the given case diff --git a/shared/src/business/utilities/setServiceIndicatorsForCase.test.js b/shared/src/business/utilities/setServiceIndicatorsForCase.test.js index 8670cc1a9f4..2c41d96551f 100644 --- a/shared/src/business/utilities/setServiceIndicatorsForCase.test.js +++ b/shared/src/business/utilities/setServiceIndicatorsForCase.test.js @@ -1,5 +1,4 @@ -import { SERVICE_INDICATOR_TYPES } from '../entities/cases/CaseConstants'; -import { User } from '../entities/User'; +import { ROLES, SERVICE_INDICATOR_TYPES } from '../entities/EntityConstants'; import { setServiceIndicatorsForCase } from './setServiceIndicatorsForCase'; let baseCaseDetail; @@ -8,7 +7,7 @@ const basePractitioner = { email: 'practitioner1@example.com', name: 'Test Practitioner', representingPrimary: true, - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, serviceIndicator: 'Paper', }; @@ -16,7 +15,7 @@ const baseRespondent = { email: 'flexionustc+respondent@gmail.com', name: 'Test Respondent', respondentId: '123-abc-123-abc', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, serviceIndicator: 'Paper', userId: 'abc-123-abc-123', }; diff --git a/shared/src/errors/errors.js b/shared/src/errors/errors.js index 99b62c3467b..fd39f7e0e10 100644 --- a/shared/src/errors/errors.js +++ b/shared/src/errors/errors.js @@ -99,9 +99,11 @@ module.exports.InvalidEntityError = class InvalidEntityError extends Error { * * @param {string} message the error message */ - constructor(message = 'entity is invalid or invalid for operation') { - super(message); - - this.statusCode = 422; + constructor( + message = 'entity is invalid or invalid for operation', + validationName, + entityIds, + ) { + super(`The ${validationName} entity was invalid. ${message}. ${entityIds}`); } }; diff --git a/shared/src/errors/errors.test.js b/shared/src/errors/errors.test.js index 3475ff01cb1..e21bd3f4580 100644 --- a/shared/src/errors/errors.test.js +++ b/shared/src/errors/errors.test.js @@ -75,15 +75,13 @@ describe('InvalidEntityError', () => { let error; beforeEach(() => { - error = new InvalidEntityError(); + error = new InvalidEntityError('Test message', 'TestEntity', 123); }); - it('should set a status code of 422', () => { - expect(error.statusCode).toEqual(422); - }); - - it('should set the message', () => { - expect(error.message).toEqual('entity is invalid or invalid for operation'); + it('should set the message which includes the entityName and failing ids', () => { + expect(error.message).toEqual( + 'The TestEntity entity was invalid. Test message. 123', + ); }); }); diff --git a/shared/src/persistence/dynamo/cases/associateUserWithCase.js b/shared/src/persistence/dynamo/cases/associateUserWithCase.js index bd1b3f958b1..89f40840148 100644 --- a/shared/src/persistence/dynamo/cases/associateUserWithCase.js +++ b/shared/src/persistence/dynamo/cases/associateUserWithCase.js @@ -3,10 +3,13 @@ const client = require('../../dynamodbClientService'); exports.associateUserWithCase = async ({ applicationContext, caseId, + userCase, userId, }) => { return client.put({ Item: { + ...userCase, + gsi1pk: `user-case|${caseId}`, pk: `user|${userId}`, sk: `case|${caseId}`, }, diff --git a/shared/src/persistence/dynamo/cases/associateUserWithCase.test.js b/shared/src/persistence/dynamo/cases/associateUserWithCase.test.js index b24c6260fc2..89df2a4366a 100644 --- a/shared/src/persistence/dynamo/cases/associateUserWithCase.test.js +++ b/shared/src/persistence/dynamo/cases/associateUserWithCase.test.js @@ -11,6 +11,7 @@ describe('associateUserWithCase', () => { userId: '123', }); expect(result).toEqual({ + gsi1pk: 'user-case|234', pk: 'user|123', sk: 'case|234', }); diff --git a/shared/src/persistence/dynamo/cases/createCase.js b/shared/src/persistence/dynamo/cases/createCase.js index 72a74598651..396133a3508 100644 --- a/shared/src/persistence/dynamo/cases/createCase.js +++ b/shared/src/persistence/dynamo/cases/createCase.js @@ -65,13 +65,6 @@ exports.createCase = async ({ applicationContext, caseToCreate }) => { applicationContext, }), ), - client.put({ - Item: { - pk: `user|${caseToCreate.userId}`, - sk: `case|${caseToCreate.caseId}`, - }, - applicationContext, - }), client.put({ Item: { pk: `case-by-docket-number|${caseToCreate.docketNumber}`, diff --git a/shared/src/persistence/dynamo/cases/getCaseByCaseId.test.js b/shared/src/persistence/dynamo/cases/getCaseByCaseId.test.js index d63e8427a59..6f11e67aa68 100644 --- a/shared/src/persistence/dynamo/cases/getCaseByCaseId.test.js +++ b/shared/src/persistence/dynamo/cases/getCaseByCaseId.test.js @@ -26,6 +26,7 @@ describe('getCaseByCaseId', () => { expect(result).toEqual({ caseId: '123', + correspondence: [], docketRecord: [], documents: [], irsPractitioners: [], @@ -78,6 +79,7 @@ describe('getCaseByCaseId', () => { expect(result).toEqual({ caseId: '123', + correspondence: [], docketRecord: [ { docketRecordId: 'abc-123', @@ -119,6 +121,7 @@ describe('getCaseByCaseId', () => { }); expect(result).toEqual({ + correspondence: [], docketRecord: [], documents: [], irsPractitioners: [], diff --git a/shared/src/persistence/dynamo/cases/getCaseByDocketNumber.test.js b/shared/src/persistence/dynamo/cases/getCaseByDocketNumber.test.js index cdf6dcbd402..43ce8b7705b 100644 --- a/shared/src/persistence/dynamo/cases/getCaseByDocketNumber.test.js +++ b/shared/src/persistence/dynamo/cases/getCaseByDocketNumber.test.js @@ -27,6 +27,7 @@ describe('getCaseByDocketNumber', () => { expect(result).toEqual({ caseId: '123', + correspondence: [], docketRecord: [], documents: [], irsPractitioners: [], @@ -78,6 +79,7 @@ describe('getCaseByDocketNumber', () => { expect(result).toEqual({ caseId: '123', + correspondence: [], docketRecord: [ { docketRecordId: 'abc-123', diff --git a/shared/src/persistence/dynamo/cases/getCasesByLeadCaseId.js b/shared/src/persistence/dynamo/cases/getCasesByLeadCaseId.js index 891743d72df..c2ed1010aa3 100644 --- a/shared/src/persistence/dynamo/cases/getCasesByLeadCaseId.js +++ b/shared/src/persistence/dynamo/cases/getCasesByLeadCaseId.js @@ -14,7 +14,7 @@ exports.getCasesByLeadCaseId = async ({ applicationContext, leadCaseId }) => { '#gsi1pk': 'gsi1pk', }, ExpressionAttributeValues: { - ':gsi1pk': leadCaseId, + ':gsi1pk': `case|${leadCaseId}`, }, IndexName: 'gsi1', KeyConditionExpression: '#gsi1pk = :gsi1pk', diff --git a/shared/src/persistence/dynamo/cases/getCasesByUser.js b/shared/src/persistence/dynamo/cases/getCasesByUser.js index 5a7572c678f..fed54c37ce8 100644 --- a/shared/src/persistence/dynamo/cases/getCasesByUser.js +++ b/shared/src/persistence/dynamo/cases/getCasesByUser.js @@ -1,5 +1,4 @@ const client = require('../../dynamodbClientService'); - const { getCaseByCaseId } = require('./getCaseByCaseId'); const { stripWorkItems } = require('../../dynamo/helpers/stripWorkItems'); diff --git a/shared/src/persistence/dynamo/cases/getCasesByUser.test.js b/shared/src/persistence/dynamo/cases/getCasesByUser.test.js index bc82de25e48..24d8ad3f70f 100644 --- a/shared/src/persistence/dynamo/cases/getCasesByUser.test.js +++ b/shared/src/persistence/dynamo/cases/getCasesByUser.test.js @@ -1,6 +1,6 @@ const client = require('../../../../../shared/src/persistence/dynamodbClientService'); const { getCasesByUser } = require('./getCasesByUser'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); const { applicationContext, @@ -18,8 +18,8 @@ applicationContext.getDocumentClient.mockReturnValue({ }); const user = { - role: User.ROLES.petitioner, - userId: 'petitioner', + role: ROLES.petitioner, + userId: '522573b0-dc40-47f7-96fd-64758da315f5', }; describe('getCasesByUser', () => { @@ -49,9 +49,11 @@ describe('getCasesByUser', () => { applicationContext, user, }); + expect(result).toEqual([ { caseId: '123', + correspondence: [], docketRecord: [], documents: [], irsPractitioners: [], diff --git a/shared/src/persistence/dynamo/cases/getClosedCasesByUser.js b/shared/src/persistence/dynamo/cases/getClosedCasesByUser.js new file mode 100644 index 00000000000..fa8a815a955 --- /dev/null +++ b/shared/src/persistence/dynamo/cases/getClosedCasesByUser.js @@ -0,0 +1,22 @@ +const { + CASE_STATUS_TYPES, +} = require('../../../business/entities/EntityConstants'); +const { getUserCases } = require('./getUserCases'); + +/** + * getClosedCasesByUser + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.userId the user id to get closed cases by + * @returns {object} the closed cases + */ +exports.getClosedCasesByUser = async ({ applicationContext, userId }) => { + const userCases = await getUserCases({ applicationContext, userId }); + + const closedCases = userCases.filter( + x => x.status === CASE_STATUS_TYPES.closed, + ); + + return closedCases; +}; diff --git a/shared/src/persistence/dynamo/cases/getClosedCasesByUser.test.js b/shared/src/persistence/dynamo/cases/getClosedCasesByUser.test.js new file mode 100644 index 00000000000..80016a01566 --- /dev/null +++ b/shared/src/persistence/dynamo/cases/getClosedCasesByUser.test.js @@ -0,0 +1,45 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { getClosedCasesByUser } = require('./getClosedCasesByUser'); +const { ROLES } = require('../../../business/entities/EntityConstants'); + +jest.mock('./getUserCases', () => ({ + getUserCases: jest.fn().mockReturnValue([ + { + caseId: '123', + pk: 'case|123', + sk: 'case|123', + status: 'New', + }, + { + caseId: '121', + pk: 'case|121', + sk: 'case|121', + status: 'Closed', + }, + ]), +})); + +const user = { + role: ROLES.petitioner, + userId: '522573b0-dc40-47f7-96fd-64758da315f5', +}; + +describe('getClosedCasesByUser', () => { + it('should filter out cases that are not closed', async () => { + const result = await getClosedCasesByUser({ + applicationContext, + user, + }); + + expect(result).toMatchObject([ + { + caseId: '121', + pk: 'case|121', + sk: 'case|121', + status: 'Closed', + }, + ]); + }); +}); diff --git a/shared/src/persistence/dynamo/cases/getOpenCasesByUser.js b/shared/src/persistence/dynamo/cases/getOpenCasesByUser.js new file mode 100644 index 00000000000..d597dc11fa6 --- /dev/null +++ b/shared/src/persistence/dynamo/cases/getOpenCasesByUser.js @@ -0,0 +1,22 @@ +const { + CASE_STATUS_TYPES, +} = require('../../../business/entities/EntityConstants'); +const { getUserCases } = require('./getUserCases'); + +/** + * getOpenCasesByUserId + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.userId the user id to get open cases by + * @returns {object} the open cases + */ +exports.getOpenCasesByUser = async ({ applicationContext, userId }) => { + const userCases = await getUserCases({ applicationContext, userId }); + + const openCases = userCases.filter( + x => x.status !== CASE_STATUS_TYPES.closed, + ); + + return openCases; +}; diff --git a/shared/src/persistence/dynamo/cases/getOpenCasesByUser.test.js b/shared/src/persistence/dynamo/cases/getOpenCasesByUser.test.js new file mode 100644 index 00000000000..9320f915597 --- /dev/null +++ b/shared/src/persistence/dynamo/cases/getOpenCasesByUser.test.js @@ -0,0 +1,45 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { getOpenCasesByUser } = require('./getOpenCasesByUser'); +const { ROLES } = require('../../../business/entities/EntityConstants'); + +jest.mock('./getUserCases', () => ({ + getUserCases: jest.fn().mockReturnValue([ + { + caseId: '123', + pk: 'case|123', + sk: 'case|123', + status: 'New', + }, + { + caseId: '121', + pk: 'case|121', + sk: 'case|121', + status: 'Closed', + }, + ]), +})); + +const user = { + role: ROLES.petitioner, + userId: '522573b0-dc40-47f7-96fd-64758da315f5', +}; + +describe('getOpenCasesByUser', () => { + it('should filter out cases that are closed', async () => { + const result = await getOpenCasesByUser({ + applicationContext, + user, + }); + + expect(result).toMatchObject([ + { + caseId: '123', + pk: 'case|123', + sk: 'case|123', + status: 'New', + }, + ]); + }); +}); diff --git a/shared/src/persistence/dynamo/cases/getUserCases.js b/shared/src/persistence/dynamo/cases/getUserCases.js new file mode 100644 index 00000000000..33a53e13e1d --- /dev/null +++ b/shared/src/persistence/dynamo/cases/getUserCases.js @@ -0,0 +1,19 @@ +const client = require('../../dynamodbClientService'); +const { stripInternalKeys } = require('../helpers/stripInternalKeys'); + +exports.getUserCases = async ({ applicationContext, userId }) => { + const userCases = await client.query({ + ExpressionAttributeNames: { + '#pk': 'pk', + '#sk': 'sk', + }, + ExpressionAttributeValues: { + ':pk': `user|${userId}`, + ':prefix': 'case', + }, + KeyConditionExpression: '#pk = :pk and begins_with(#sk, :prefix)', + applicationContext, + }); + + return stripInternalKeys(userCases); +}; diff --git a/shared/src/persistence/dynamo/cases/getUserCases.test.js b/shared/src/persistence/dynamo/cases/getUserCases.test.js new file mode 100644 index 00000000000..f9fe5924a87 --- /dev/null +++ b/shared/src/persistence/dynamo/cases/getUserCases.test.js @@ -0,0 +1,51 @@ +const client = require('../../dynamodbClientService'); +const { getUserCases } = require('./getUserCases'); +const { ROLES } = require('../../../business/entities/EntityConstants'); + +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); + +let queryStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [], + }), +}); + +applicationContext.getDocumentClient.mockReturnValue({ + query: queryStub, +}); + +const user = { + role: ROLES.petitioner, + userId: '522573b0-dc40-47f7-96fd-64758da315f5', +}; + +describe('getUserCases', () => { + beforeEach(() => { + client.query = jest.fn().mockReturnValueOnce([ + { + caseId: '123', + leadCaseId: '321', + pk: 'user|123', + sk: 'case|123', + status: 'New', + }, + ]); + }); + + it('should return data with stripped internal keys from persistence', async () => { + const result = await getUserCases({ + applicationContext, + user, + }); + + expect(result).toEqual([ + { + caseId: '123', + leadCaseId: '321', + status: 'New', + }, + ]); + }); +}); diff --git a/shared/src/persistence/dynamo/cases/updateCase.js b/shared/src/persistence/dynamo/cases/updateCase.js index be79a9e5a50..81e57ade36b 100644 --- a/shared/src/persistence/dynamo/cases/updateCase.js +++ b/shared/src/persistence/dynamo/cases/updateCase.js @@ -234,8 +234,45 @@ exports.updateCase = async ({ applicationContext, caseToUpdate }) => { } } + // update user-case mappings + if ( + oldCase.status !== caseToUpdate.status || + oldCase.docketNumberSuffix !== caseToUpdate.docketNumberSuffix || + oldCase.caseCaption !== caseToUpdate.caseCaption || + oldCase.leadCaseId !== caseToUpdate.leadCaseId + ) { + const userCaseMappings = await client.query({ + ExpressionAttributeNames: { + '#gsi1pk': 'gsi1pk', + }, + ExpressionAttributeValues: { + ':gsi1pk': `user-case|${caseToUpdate.caseId}`, + }, + IndexName: 'gsi1', + KeyConditionExpression: '#gsi1pk = :gsi1pk', + applicationContext, + }); + + for (let userCaseItem of userCaseMappings) { + requests.push( + client.put({ + Item: { + ...userCaseItem, + caseCaption: caseToUpdate.caseCaption, + docketNumberSuffix: caseToUpdate.docketNumberSuffix, + docketNumberWithSuffix: caseToUpdate.docketNumberWithSuffix, + gsi1pk: `user-case|${caseToUpdate.caseId}`, + leadCaseId: caseToUpdate.leadCaseId, + status: caseToUpdate.status, + }, + applicationContext, + }), + ); + } + } + let setLeadCase = caseToUpdate.leadCaseId - ? { gsi1pk: caseToUpdate.leadCaseId } + ? { gsi1pk: `case|${caseToUpdate.leadCaseId}` } : {}; await Promise.all([ diff --git a/shared/src/persistence/dynamo/cases/updateCase.test.js b/shared/src/persistence/dynamo/cases/updateCase.test.js index 62570e2e40f..9ed92669ec5 100644 --- a/shared/src/persistence/dynamo/cases/updateCase.test.js +++ b/shared/src/persistence/dynamo/cases/updateCase.test.js @@ -2,11 +2,14 @@ const client = require('../../dynamodbClientService'); const { applicationContext, } = require('../../../business/test/createTestApplicationContext'); -const { Case } = require('../../../business/entities/cases/Case'); +const { + CASE_STATUS_TYPES, +} = require('../../../business/entities/EntityConstants'); const { updateCase } = require('./updateCase'); describe('updateCase', () => { let firstQueryStub; + let secondQueryStub; beforeEach(() => { firstQueryStub = [ @@ -15,7 +18,17 @@ describe('updateCase', () => { inProgress: false, pk: 'case|123', sk: 'case|123', - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, + }, + ]; + + secondQueryStub = [ + { + gsi1pk: 'user-case|123', + leadCaseId: '123', + pk: 'user|123', + sk: 'case|123', + status: CASE_STATUS_TYPES.generalDocket, }, ]; @@ -30,6 +43,7 @@ describe('updateCase', () => { applicationContext .getDocumentClient() .query.mockReturnValueOnce(firstQueryStub) + .mockReturnValueOnce(secondQueryStub) .mockReturnValue([ { sk: '123', @@ -64,7 +78,7 @@ describe('updateCase', () => { caseId: '123', docketNumber: '101-18', docketNumberSuffix: null, - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, userId: 'petitioner', }, }); @@ -87,14 +101,14 @@ describe('updateCase', () => { docketNumber: '101-18', docketNumberSuffix: 'W', inProgress: true, - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: '2019-03-01T21:40:46.415Z', userId: 'petitioner', }, }); expect( - applicationContext.getDocumentClient().put.mock.calls[0][0].Item, + applicationContext.getDocumentClient().put.mock.calls[1][0].Item, ).toMatchObject({ pk: 'case|123', sk: 'case|123', @@ -103,7 +117,7 @@ describe('updateCase', () => { applicationContext.getDocumentClient().update.mock.calls[0][0], ).toMatchObject({ ExpressionAttributeValues: { - ':caseStatus': Case.STATUS_TYPES.calendared, + ':caseStatus': CASE_STATUS_TYPES.calendared, }, }); expect( @@ -150,7 +164,7 @@ describe('updateCase', () => { associatedJudge: 'Judge Buch', caseId: '123', docketNumberSuffix: null, - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -169,7 +183,7 @@ describe('updateCase', () => { caseToUpdate: { caseId: '123', docketNumberSuffix: null, - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -192,7 +206,7 @@ describe('updateCase', () => { irsPractitioners: [ { name: 'Guy Fieri', userId: 'user-id-existing-234' }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -223,7 +237,7 @@ describe('updateCase', () => { { name: 'Guy Fieri', userId: 'user-id-existing-123' }, { name: 'Rachel Ray', userId: 'user-id-existing-234' }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -267,7 +281,7 @@ describe('updateCase', () => { }, { name: 'Rachel Ray', userId: 'user-id-existing-234' }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); expect( @@ -309,7 +323,7 @@ describe('updateCase', () => { userId: 'user-id-existing-234', }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -336,7 +350,7 @@ describe('updateCase', () => { privatePractitioners: [ { name: 'Guy Fieri', userId: 'user-id-existing-234' }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -369,7 +383,7 @@ describe('updateCase', () => { { name: 'Guy Fieri', userId: 'user-id-existing-123' }, { name: 'Rachel Ray', userId: 'user-id-existing-234' }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -402,7 +416,7 @@ describe('updateCase', () => { }, { name: 'Rachel Ray', userId: 'user-id-existing-234' }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -431,7 +445,7 @@ describe('updateCase', () => { privatePractitioners: [ { name: 'Rachel Ray', userId: 'user-id-existing-234' }, ], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, }); @@ -447,4 +461,118 @@ describe('updateCase', () => { }); }); }); + + describe('user case mappings', () => { + beforeEach(() => { + applicationContext.getDocumentClient().query = jest + .fn() + .mockResolvedValueOnce(firstQueryStub) // getting case + .mockResolvedValueOnce([]) // work item mappings + .mockResolvedValue(secondQueryStub); + + client.query = applicationContext.getDocumentClient().query; + }); + + it('updates user case mapping if the status has changed', async () => { + await updateCase({ + applicationContext, + caseToUpdate: { + associatedJudge: 'Judge Buch', + caseId: '123', + docketNumber: '101-18', + docketNumberSuffix: null, + inProgress: true, + status: CASE_STATUS_TYPES.calendared, + trialDate: '2019-03-01T21:40:46.415Z', + userId: 'petitioner', + }, + }); + + expect( + applicationContext.getDocumentClient().put.mock.calls[0][0].Item, + ).toMatchObject({ + gsi1pk: 'user-case|123', + pk: 'user|123', + sk: 'case|123', + status: CASE_STATUS_TYPES.calendared, + }); + }); + + it('updates user case mapping if the docket number suffix has changed', async () => { + await updateCase({ + applicationContext, + caseToUpdate: { + associatedJudge: 'Judge Buch', + caseId: '123', + docketNumber: '101-18', + docketNumberSuffix: 'W', + inProgress: true, + status: CASE_STATUS_TYPES.generalDocket, + trialDate: '2019-03-01T21:40:46.415Z', + userId: 'petitioner', + }, + }); + + expect( + applicationContext.getDocumentClient().put.mock.calls[0][0].Item, + ).toMatchObject({ + docketNumberSuffix: 'W', + gsi1pk: 'user-case|123', + pk: 'user|123', + sk: 'case|123', + }); + }); + + it('updates user case mapping if the case caption has changed', async () => { + await updateCase({ + applicationContext, + caseToUpdate: { + associatedJudge: 'Judge Buch', + caseCaption: 'Guy Fieri, Petitioner', + caseId: '123', + docketNumber: '101-18', + docketNumberSuffix: null, + inProgress: true, + status: CASE_STATUS_TYPES.generalDocket, + trialDate: '2019-03-01T21:40:46.415Z', + userId: 'petitioner', + }, + }); + + expect( + applicationContext.getDocumentClient().put.mock.calls[0][0].Item, + ).toMatchObject({ + caseCaption: 'Guy Fieri, Petitioner', + gsi1pk: 'user-case|123', + pk: 'user|123', + sk: 'case|123', + }); + }); + + it('updates user case mapping if the lead case id (consolidation) has changed', async () => { + await updateCase({ + applicationContext, + caseToUpdate: { + associatedJudge: 'Judge Buch', + caseId: '123', + docketNumber: '101-18', + docketNumberSuffix: null, + inProgress: true, + leadCaseId: 'case|321', + status: CASE_STATUS_TYPES.generalDocket, + trialDate: '2019-03-01T21:40:46.415Z', + userId: 'petitioner', + }, + }); + + expect( + applicationContext.getDocumentClient().put.mock.calls[0][0].Item, + ).toMatchObject({ + gsi1pk: 'user-case|123', + leadCaseId: 'case|321', + pk: 'user|123', + sk: 'case|123', + }); + }); + }); }); diff --git a/shared/src/persistence/dynamo/cases/updateWorkItemInCase.test.js b/shared/src/persistence/dynamo/cases/updateWorkItemInCase.test.js index 8044e4069ca..0f13adfa3b6 100644 --- a/shared/src/persistence/dynamo/cases/updateWorkItemInCase.test.js +++ b/shared/src/persistence/dynamo/cases/updateWorkItemInCase.test.js @@ -28,13 +28,16 @@ describe('updateWorkItemInCase', () => { ], }, workItem: { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', workItemId: '456', }, }); expect(updateStub.mock.calls[0][0]).toMatchObject({ ExpressionAttributeValues: { - ':workItem': { assigneeId: 'bob', workItemId: '456' }, + ':workItem': { + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', + workItemId: '456', + }, }, Key: { pk: 'case|123', diff --git a/shared/src/persistence/dynamo/correspondence/deleteCaseCorrespondence.js b/shared/src/persistence/dynamo/correspondence/deleteCaseCorrespondence.js new file mode 100644 index 00000000000..099f37d6615 --- /dev/null +++ b/shared/src/persistence/dynamo/correspondence/deleteCaseCorrespondence.js @@ -0,0 +1,24 @@ +const client = require('../../dynamodbClientService'); + +/** + * deleteCaseCorrespondence + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case the correspondence is attached to + * @param {string} providers.documentIdToDelete the id of the correspondence document to delete + * @returns {Array} the promises for the persistence delete calls + */ +exports.deleteCaseCorrespondence = async ({ + applicationContext, + caseId, + documentId, +}) => { + return await client.delete({ + applicationContext, + key: { + pk: `case|${caseId}`, + sk: `correspondence|${documentId}`, + }, + }); +}; diff --git a/shared/src/persistence/dynamo/correspondence/deleteCaseCorrespondence.test.js b/shared/src/persistence/dynamo/correspondence/deleteCaseCorrespondence.test.js new file mode 100644 index 00000000000..08a4e7ebd8c --- /dev/null +++ b/shared/src/persistence/dynamo/correspondence/deleteCaseCorrespondence.test.js @@ -0,0 +1,28 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { deleteCaseCorrespondence } = require('./deleteCaseCorrespondence'); + +describe('deleteCaseCorrespondence', () => { + beforeAll(() => { + applicationContext.environment.stage = 'dev'; + }); + + it('should delete the specified correspondence record', async () => { + await deleteCaseCorrespondence({ + applicationContext, + caseId: '234', + documentId: '123', + }); + + expect( + applicationContext.getDocumentClient().delete.mock.calls[0][0], + ).toMatchObject({ + Key: { + pk: 'case|234', + sk: 'correspondence|123', + }, + TableName: 'efcms-dev', + }); + }); +}); diff --git a/shared/src/persistence/dynamo/docketRecord/updateDocketRecord.js b/shared/src/persistence/dynamo/docketRecord/updateDocketRecord.js new file mode 100644 index 00000000000..d3bbb552ee6 --- /dev/null +++ b/shared/src/persistence/dynamo/docketRecord/updateDocketRecord.js @@ -0,0 +1,17 @@ +const client = require('../../dynamodbClientService'); + +exports.updateDocketRecord = async ({ + applicationContext, + caseId, + docketRecord, + docketRecordId, +}) => { + await client.put({ + Item: { + pk: `case|${caseId}`, + sk: `docket-record|${docketRecordId}`, + ...docketRecord, + }, + applicationContext, + }); +}; diff --git a/shared/src/persistence/dynamo/docketRecord/updateDocketRecord.test.js b/shared/src/persistence/dynamo/docketRecord/updateDocketRecord.test.js new file mode 100644 index 00000000000..6396f8ee70d --- /dev/null +++ b/shared/src/persistence/dynamo/docketRecord/updateDocketRecord.test.js @@ -0,0 +1,35 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { updateDocketRecord } = require('./updateDocketRecord'); + +const mockDocketRecordId = '9b52c605-edba-41d7-b045-d5f992a499d3'; +const mockCaseId = '9b52c506-edba-41d7-b045-d5f992a499d3'; + +const mockDocketRecord = { + docketRecordId: mockDocketRecordId, + documentTitle: 'Title of le Document', + filedBy: 'The one and only, Guy Fieri', + status: 'complete', +}; + +describe('updateDocketRecord', () => { + it('makes put request with the given docket record data for the matching docket record id', async () => { + await updateDocketRecord({ + applicationContext, + caseId: mockCaseId, + docketRecord: mockDocketRecord, + docketRecordId: mockDocketRecordId, + }); + + expect( + applicationContext.getDocumentClient().put.mock.calls[0][0], + ).toMatchObject({ + Item: { + pk: `case|${mockCaseId}`, + sk: `docket-record|${mockDocketRecordId}`, + ...mockDocketRecord, + }, + }); + }); +}); diff --git a/shared/src/persistence/dynamo/documents/updateDocument.js b/shared/src/persistence/dynamo/documents/updateDocument.js new file mode 100644 index 00000000000..cdbc1bb1d57 --- /dev/null +++ b/shared/src/persistence/dynamo/documents/updateDocument.js @@ -0,0 +1,17 @@ +const client = require('../../dynamodbClientService'); + +exports.updateDocument = async ({ + applicationContext, + caseId, + document, + documentId, +}) => { + await client.put({ + Item: { + pk: `case|${caseId}`, + sk: `document|${documentId}`, + ...document, + }, + applicationContext, + }); +}; diff --git a/shared/src/persistence/dynamo/documents/updateDocument.test.js b/shared/src/persistence/dynamo/documents/updateDocument.test.js new file mode 100644 index 00000000000..f39a2da0393 --- /dev/null +++ b/shared/src/persistence/dynamo/documents/updateDocument.test.js @@ -0,0 +1,35 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { updateDocument } = require('./updateDocument'); + +const mockDocumentId = '9b52c605-edba-41d7-b045-d5f992a499d3'; +const mockCaseId = '9b52c506-edba-41d7-b045-d5f992a499d3'; + +const mockDocument = { + documentId: mockDocumentId, + documentTitle: 'Title of le Document', + filedBy: 'The one and only, Guy Fieri', + status: 'complete', +}; + +describe('updateDocument', () => { + it('makes put request with the given document data for the matching document id', async () => { + await updateDocument({ + applicationContext, + caseId: mockCaseId, + document: mockDocument, + documentId: mockDocumentId, + }); + + expect( + applicationContext.getDocumentClient().put.mock.calls[0][0], + ).toMatchObject({ + Item: { + pk: `case|${mockCaseId}`, + sk: `document|${mockDocumentId}`, + ...mockDocument, + }, + }); + }); +}); diff --git a/shared/src/persistence/dynamo/elasticsearch/getElasticsearchReindexRecords.js b/shared/src/persistence/dynamo/elasticsearch/getElasticsearchReindexRecords.js index e90426bad44..a7757e81ccb 100644 --- a/shared/src/persistence/dynamo/elasticsearch/getElasticsearchReindexRecords.js +++ b/shared/src/persistence/dynamo/elasticsearch/getElasticsearchReindexRecords.js @@ -3,8 +3,9 @@ const client = require('../../dynamodbClientService'); /** * getElasticsearchReindexRecords * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context + * @param {object} arguments deconstructed arguments + * @param {object} arguments.applicationContext the application context + * @returns {Promise} resolved with query results */ exports.getElasticsearchReindexRecords = async ({ applicationContext }) => { return await client.query({ diff --git a/shared/src/persistence/dynamo/elasticsearch/getRecord.js b/shared/src/persistence/dynamo/elasticsearch/getRecord.js index 672fc4955d6..b0a0e5af152 100644 --- a/shared/src/persistence/dynamo/elasticsearch/getRecord.js +++ b/shared/src/persistence/dynamo/elasticsearch/getRecord.js @@ -3,10 +3,11 @@ const client = require('../../dynamodbClientService'); /** * getRecord - get a generic record from dynamo to index in elasticsearch * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {object} providers.recordPk the pk of the record to get - * @param {object} providers.recordSk the sk of the record to get + * @param {object} arguments deconstructed arguments + * @param {object} arguments.applicationContext the application context + * @param {string} arguments.recordPk the pk of the record to get + * @param {string} arguments.recordSk the sk of the record to get + * @returns {Promise} resolves with result of query */ exports.getRecord = async ({ applicationContext, recordPk, recordSk }) => { return await client.get({ diff --git a/shared/src/persistence/dynamo/helpers/aggregateCaseItems.js b/shared/src/persistence/dynamo/helpers/aggregateCaseItems.js index 428243d3edd..4eeeb06a6fc 100644 --- a/shared/src/persistence/dynamo/helpers/aggregateCaseItems.js +++ b/shared/src/persistence/dynamo/helpers/aggregateCaseItems.js @@ -17,12 +17,17 @@ exports.aggregateCaseItems = caseAndCaseItems => { const irsPractitioners = caseAndCaseItems.filter(item => item.sk.startsWith('irsPractitioner|'), ); + const correspondences = caseAndCaseItems.filter(item => + item.sk.startsWith('correspondence|'), + ); const sortedDocketRecord = sortBy(docketRecord, 'index'); const sortedDocuments = sortBy(documents, 'createdAt'); + const sortedCorrespondences = sortBy(correspondences, 'filingDate'); return { ...theCase, + correspondence: sortedCorrespondences, docketRecord: sortedDocketRecord, documents: sortedDocuments, irsPractitioners, diff --git a/shared/src/persistence/dynamo/messages/createCaseMessage.js b/shared/src/persistence/dynamo/messages/createCaseMessage.js new file mode 100644 index 00000000000..94f3cea8d47 --- /dev/null +++ b/shared/src/persistence/dynamo/messages/createCaseMessage.js @@ -0,0 +1,21 @@ +const { put } = require('../../dynamodbClientService'); + +/** + * createCaseMessage + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {object} providers.caseMessage the case message data + * @returns {object} the created case message + */ +exports.createCaseMessage = async ({ applicationContext, caseMessage }) => { + await put({ + Item: { + gsi1pk: `message|${caseMessage.messageId}`, + pk: `case|${caseMessage.caseId}`, + sk: `message|${caseMessage.messageId}`, + ...caseMessage, + }, + applicationContext, + }); +}; diff --git a/shared/src/persistence/dynamo/messages/createCaseMessage.test.js b/shared/src/persistence/dynamo/messages/createCaseMessage.test.js new file mode 100644 index 00000000000..a5845b9c066 --- /dev/null +++ b/shared/src/persistence/dynamo/messages/createCaseMessage.test.js @@ -0,0 +1,46 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { createCaseMessage } = require('./createCaseMessage'); + +const mockCaseMessage = { + caseId: 'b3f09a45-b27c-4383-acc1-2ab1f99e6725', + createdAt: '2019-03-01T21:40:46.415Z', + from: 'Test Petitionsclerk', + fromSection: 'petitions', + fromUserId: '4791e892-14ee-4ab1-8468-0c942ec379d2', + message: 'hey there', + messageId: 'a10d6855-f3ee-4c11-861c-c7f11cba4dff', + subject: 'hello', + to: 'Test Petitionsclerk2', + toSection: 'petitions', + toUserId: '449b916e-3362-4a5d-bf56-b2b94ba29c12', +}; + +describe('createCaseMessage', () => { + beforeAll(() => { + applicationContext.environment.stage = 'dev'; + applicationContext.getDocumentClient().put.mockReturnValue({ + promise: () => Promise.resolve(null), + }); + }); + + it('attempts to persist the case message record', async () => { + await createCaseMessage({ + applicationContext, + caseMessage: mockCaseMessage, + }); + + expect( + applicationContext.getDocumentClient().put.mock.calls.length, + ).toEqual(1); + expect( + applicationContext.getDocumentClient().put.mock.calls[0][0].Item, + ).toMatchObject({ + gsi1pk: `message|${mockCaseMessage.messageId}`, + pk: `case|${mockCaseMessage.caseId}`, + sk: `message|${mockCaseMessage.messageId}`, + ...mockCaseMessage, + }); + }); +}); diff --git a/shared/src/persistence/dynamo/messages/getCaseMessageById.js b/shared/src/persistence/dynamo/messages/getCaseMessageById.js new file mode 100644 index 00000000000..2fd2f0aa99d --- /dev/null +++ b/shared/src/persistence/dynamo/messages/getCaseMessageById.js @@ -0,0 +1,25 @@ +const { query } = require('../../dynamodbClientService'); + +/** + * getCaseMessageById + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.messageId the message id + * @returns {object} the created case message + */ +exports.getCaseMessageById = async ({ applicationContext, messageId }) => { + const messages = await query({ + ExpressionAttributeNames: { + '#gsi1pk': 'gsi1pk', + }, + ExpressionAttributeValues: { + ':gsi1pk': `message|${messageId}`, + }, + IndexName: 'gsi1', + KeyConditionExpression: '#gsi1pk = :gsi1pk', + applicationContext, + }); + + return messages[0]; +}; diff --git a/shared/src/persistence/dynamo/messages/getCaseMessageById.test.js b/shared/src/persistence/dynamo/messages/getCaseMessageById.test.js new file mode 100644 index 00000000000..2976dd980f0 --- /dev/null +++ b/shared/src/persistence/dynamo/messages/getCaseMessageById.test.js @@ -0,0 +1,36 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { getCaseMessageById } = require('./getCaseMessageById'); + +const mockCaseMessage = { + caseId: 'b3f09a45-b27c-4383-acc1-2ab1f99e6725', + createdAt: '2019-03-01T21:40:46.415Z', + from: 'Test Petitionsclerk', + fromSection: 'petitions', + fromUserId: '4791e892-14ee-4ab1-8468-0c942ec379d2', + message: 'hey there', + messageId: 'a10d6855-f3ee-4c11-861c-c7f11cba4dff', + subject: 'hello', + to: 'Test Petitionsclerk2', + toSection: 'petitions', + toUserId: '449b916e-3362-4a5d-bf56-b2b94ba29c12', +}; + +describe('getCaseMessageById', () => { + beforeAll(() => { + applicationContext.environment.stage = 'dev'; + applicationContext.getDocumentClient().query.mockReturnValue({ + promise: () => Promise.resolve({ Items: [mockCaseMessage] }), + }); + }); + + it('retrieves the case message from persistence', async () => { + const retrievedMessage = await getCaseMessageById({ + applicationContext, + messageId: mockCaseMessage.messageId, + }); + + expect(retrievedMessage).toEqual(mockCaseMessage); + }); +}); diff --git a/shared/src/persistence/dynamo/notifications/getWebSocketConnectionsByUserId.test.js b/shared/src/persistence/dynamo/notifications/getWebSocketConnectionsByUserId.test.js index fdf385ae9fe..cc717b01860 100644 --- a/shared/src/persistence/dynamo/notifications/getWebSocketConnectionsByUserId.test.js +++ b/shared/src/persistence/dynamo/notifications/getWebSocketConnectionsByUserId.test.js @@ -9,7 +9,7 @@ describe('getWebSocketConnectionsByUserId', () => { it('attempts to retrieve the connections for a user', async () => { const result = await getWebSocketConnectionsByUserId({ applicationContext, - userId: '123', + userId: 'a66ac519-fd1a-44ac-8226-b4a53d348677', }); expect(applicationContext.getDocumentClient().query).toHaveBeenCalled(); diff --git a/shared/src/persistence/dynamo/notifications/saveUserConnection.test.js b/shared/src/persistence/dynamo/notifications/saveUserConnection.test.js index 89479d76957..626c0626a41 100644 --- a/shared/src/persistence/dynamo/notifications/saveUserConnection.test.js +++ b/shared/src/persistence/dynamo/notifications/saveUserConnection.test.js @@ -17,7 +17,7 @@ describe('saveUserConnection', () => { applicationContext, connectionId: 'abc', endpoint: {}, - userId: '123', + userId: 'a66ac519-fd1a-44ac-8226-b4a53d348677', }); expect(applicationContext.getDocumentClient().put).toHaveBeenCalledWith({ @@ -25,7 +25,7 @@ describe('saveUserConnection', () => { connectionId: 'abc', endpoint: {}, gsi1pk: 'connection|abc', - pk: 'user|123', + pk: 'user|a66ac519-fd1a-44ac-8226-b4a53d348677', sk: 'connection|abc', ttl: expect.anything(), }, diff --git a/shared/src/persistence/dynamo/trialSessions/createTrialSessionWorkingCopy.test.js b/shared/src/persistence/dynamo/trialSessions/createTrialSessionWorkingCopy.test.js index 669982e0510..d4c026dcef5 100644 --- a/shared/src/persistence/dynamo/trialSessions/createTrialSessionWorkingCopy.test.js +++ b/shared/src/persistence/dynamo/trialSessions/createTrialSessionWorkingCopy.test.js @@ -5,9 +5,11 @@ const { createTrialSessionWorkingCopy, } = require('./createTrialSessionWorkingCopy'); +const userId = 'a66ac519-fd1a-44ac-8226-b4a53d348677'; + const mockTrialSessionWorkingCopy = { trialSessionId: '456', - userId: '123', + userId, }; describe('createTrialSessionWorkingCopy', () => { @@ -26,9 +28,9 @@ describe('createTrialSessionWorkingCopy', () => { ).toMatchObject({ Item: { pk: 'trial-session-working-copy|456', - sk: 'user|123', + sk: `user|${userId}`, trialSessionId: '456', - userId: '123', + userId, }, TableName: 'efcms-dev', }); diff --git a/shared/src/persistence/dynamo/trialSessions/getCalendaredCasesForTrialSession.test.js b/shared/src/persistence/dynamo/trialSessions/getCalendaredCasesForTrialSession.test.js index f43e860550d..5dec4519d59 100644 --- a/shared/src/persistence/dynamo/trialSessions/getCalendaredCasesForTrialSession.test.js +++ b/shared/src/persistence/dynamo/trialSessions/getCalendaredCasesForTrialSession.test.js @@ -55,6 +55,7 @@ describe('getCalendaredCasesForTrialSession', () => { expect(result).toEqual([ { caseId: '123', + correspondence: [], disposition: 'something', docketRecord: [ { diff --git a/shared/src/persistence/dynamo/users/createPractitionerUser.js b/shared/src/persistence/dynamo/users/createPractitionerUser.js index 236f3a824f4..86a721d8902 100644 --- a/shared/src/persistence/dynamo/users/createPractitionerUser.js +++ b/shared/src/persistence/dynamo/users/createPractitionerUser.js @@ -1,5 +1,5 @@ const client = require('../../dynamodbClientService'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); exports.createUserRecords = async ({ applicationContext, user, userId }) => { delete user.password; @@ -53,11 +53,7 @@ exports.createUserRecords = async ({ applicationContext, user, userId }) => { exports.createPractitionerUser = async ({ applicationContext, user }) => { let userId = applicationContext.getUniqueId(); - if ( - ![User.ROLES.privatePractitioner, User.ROLES.irsPractitioner].includes( - user.role, - ) - ) { + if (![ROLES.privatePractitioner, ROLES.irsPractitioner].includes(user.role)) { throw new Error( 'Practitioner users must have either private or IRS practitioner role', ); diff --git a/shared/src/persistence/dynamo/users/createPractitionerUser.test.js b/shared/src/persistence/dynamo/users/createPractitionerUser.test.js index 163f6842e07..ca9d0ac619e 100644 --- a/shared/src/persistence/dynamo/users/createPractitionerUser.test.js +++ b/shared/src/persistence/dynamo/users/createPractitionerUser.test.js @@ -5,14 +5,14 @@ const { createPractitionerUser, createUserRecords, } = require('./createPractitionerUser'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); const userId = '9b52c605-edba-41d7-b045-d5f992a499d3'; const privatePractitionerUser = { barNumber: 'pt1234', //intentionally lower case - should be converted to upper case when persisted name: 'Test Private Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', }; @@ -20,14 +20,14 @@ const privatePractitionerUserWithSection = { barNumber: 'pt1234', email: 'test@example.com', name: 'Test Private Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', }; const privatePractitionerUserWithoutBarNumber = { barNumber: '', name: 'Test Private Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', }; @@ -35,7 +35,7 @@ const otherUser = { barNumber: 'pt1234', email: 'test@example.com', name: 'Test Other', - role: User.ROLES.other, + role: ROLES.other, section: 'other', }; diff --git a/shared/src/persistence/dynamo/users/createUser.js b/shared/src/persistence/dynamo/users/createUser.js index 610bd5778a7..5225b0901be 100644 --- a/shared/src/persistence/dynamo/users/createUser.js +++ b/shared/src/persistence/dynamo/users/createUser.js @@ -1,5 +1,5 @@ const client = require('../../dynamodbClientService'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); exports.createUserRecords = async ({ applicationContext, user, userId }) => { delete user.password; @@ -17,7 +17,7 @@ exports.createUserRecords = async ({ applicationContext, user, userId }) => { applicationContext, }); - if (user.role === User.ROLES.judge) { + if (user.role === ROLES.judge) { await client.put({ Item: { pk: 'section|judge', @@ -39,8 +39,8 @@ exports.createUserRecords = async ({ applicationContext, user, userId }) => { }); if ( - (user.role === User.ROLES.privatePractitioner || - user.role === User.ROLES.irsPractitioner) && + (user.role === ROLES.privatePractitioner || + user.role === ROLES.irsPractitioner) && user.name && user.barNumber ) { @@ -70,7 +70,7 @@ exports.createUserRecords = async ({ applicationContext, user, userId }) => { exports.createUser = async ({ applicationContext, user }) => { let userId; let userPoolId = - user.role === User.ROLES.irsSuperuser + user.role === ROLES.irsSuperuser ? process.env.USER_POOL_IRS_ID : process.env.USER_POOL_ID; diff --git a/shared/src/persistence/dynamo/users/createUser.test.js b/shared/src/persistence/dynamo/users/createUser.test.js index d771b928d3f..87fff5f0345 100644 --- a/shared/src/persistence/dynamo/users/createUser.test.js +++ b/shared/src/persistence/dynamo/users/createUser.test.js @@ -2,27 +2,27 @@ const { applicationContext, } = require('../../../business/test/createTestApplicationContext'); const { createUser, createUserRecords } = require('./createUser'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); const userId = '9b52c605-edba-41d7-b045-d5f992a499d3'; const petitionsClerkUser = { name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', }; const privatePractitionerUser = { barNumber: 'pt1234', //intentionally lower case - should be converted to upper case when persisted name: 'Test Private Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', }; const privatePractitionerUserWithoutBarNumber = { barNumber: '', name: 'Test Private Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', }; @@ -56,7 +56,7 @@ describe('createUser', () => { it('should call adminCreateUser', async () => { const petitionsclerkUser = { name: 'Test Petitionsclerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', }; @@ -166,7 +166,7 @@ describe('createUser', () => { it('attempts to persist a judge user with a section mapping record for the chambers and the judge', async () => { const judgeUser = { name: 'Judge Adam', - role: User.ROLES.judge, + role: ROLES.judge, section: 'adamsChambers', }; @@ -319,7 +319,7 @@ describe('createUser', () => { it('does not persist section mapping record if user does not have a section', async () => { const privatePractitionerUserWithoutSection = { name: 'Test Private Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }; await createUserRecords({ diff --git a/shared/src/persistence/dynamo/users/getInternalUsers.js b/shared/src/persistence/dynamo/users/getInternalUsers.js index 09333c80766..8768c31689c 100644 --- a/shared/src/persistence/dynamo/users/getInternalUsers.js +++ b/shared/src/persistence/dynamo/users/getInternalUsers.js @@ -2,7 +2,7 @@ const { ADC_SECTION, DOCKET_SECTION, PETITIONS_SECTION, -} = require('../../../business/entities/WorkQueue'); +} = require('../../../business/entities/EntityConstants'); const { getUsersInSection } = require('./getUsersInSection'); exports.getInternalUsers = async ({ applicationContext }) => { diff --git a/shared/src/persistence/dynamo/users/getPractitionerByBarNumber.test.js b/shared/src/persistence/dynamo/users/getPractitionerByBarNumber.test.js index b58cbb5cb7e..9076e2fc009 100644 --- a/shared/src/persistence/dynamo/users/getPractitionerByBarNumber.test.js +++ b/shared/src/persistence/dynamo/users/getPractitionerByBarNumber.test.js @@ -2,7 +2,7 @@ const client = require('../../../../../shared/src/persistence/dynamodbClientServ const { applicationContext, } = require('../../../business/test/createTestApplicationContext'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); const { getPractitionerByBarNumber } = require('./getPractitionerByBarNumber'); @@ -11,7 +11,7 @@ describe('getPractitionerByBarNumber', () => { barNumber: 'PT1234', name: 'Test Practitioner', pk: 'user|9805d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', sk: '9805d1ab-18d0-43ec-bafb-654e83405416', userId: '9805d1ab-18d0-43ec-bafb-654e83405416', @@ -21,7 +21,7 @@ describe('getPractitionerByBarNumber', () => { barNumber: 'PI5678', name: 'IRS Practitioner', pk: 'user|0105d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, section: 'irsPractitioner', sk: '0105d1ab-18d0-43ec-bafb-654e83405416', userId: '0105d1ab-18d0-43ec-bafb-654e83405416', @@ -31,7 +31,7 @@ describe('getPractitionerByBarNumber', () => { barNumber: 'PI9999', name: 'Inactive Practitioner', pk: 'user|be4274f0-c525-45bc-8378-9f30fd841571', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, section: 'inactivePractitioner', sk: 'be4274f0-c525-45bc-8378-9f30fd841571', userId: 'be4274f0-c525-45bc-8378-9f30fd841571', diff --git a/shared/src/persistence/dynamo/users/getUsersBySearchKey.test.js b/shared/src/persistence/dynamo/users/getUsersBySearchKey.test.js index c6981b49798..27826381892 100644 --- a/shared/src/persistence/dynamo/users/getUsersBySearchKey.test.js +++ b/shared/src/persistence/dynamo/users/getUsersBySearchKey.test.js @@ -1,6 +1,6 @@ const client = require('../../../../../shared/src/persistence/dynamodbClientService'); const { getUsersBySearchKey } = require('./getUsersBySearchKey'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); const { applicationContext, @@ -13,7 +13,7 @@ describe('getUsersBySearchKey', () => { barNumber: 'PT1234', name: 'Test Practitioner', pk: 'user|9805d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', sk: '9805d1ab-18d0-43ec-bafb-654e83405416', userId: '9805d1ab-18d0-43ec-bafb-654e83405416', @@ -38,7 +38,7 @@ describe('getUsersBySearchKey', () => { barNumber: 'PT1234', name: 'Test Practitioner', pk: 'user|9805d1ab-18d0-43ec-bafb-654e83405416', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', sk: '9805d1ab-18d0-43ec-bafb-654e83405416', userId: '9805d1ab-18d0-43ec-bafb-654e83405416', diff --git a/shared/src/persistence/dynamo/users/updatePractitionerUser.test.js b/shared/src/persistence/dynamo/users/updatePractitionerUser.test.js index 7fe57521bd0..422692375cc 100644 --- a/shared/src/persistence/dynamo/users/updatePractitionerUser.test.js +++ b/shared/src/persistence/dynamo/users/updatePractitionerUser.test.js @@ -5,7 +5,7 @@ const { updatePractitionerUser, updateUserRecords, } = require('./updatePractitionerUser'); -const { User } = require('../../../business/entities/User'); +const { ROLES } = require('../../../business/entities/EntityConstants'); const userId = '9b52c605-edba-41d7-b045-d5f992a499d3'; @@ -14,13 +14,13 @@ describe('updatePractitionerUser', () => { const oldUser = { barNumber: 'PT1234', name: 'Test Private Practitioner', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, section: 'privatePractitioner', }; const updatedUser = { barNumber: 'PT1234', name: 'Test Private Practitioner', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, section: 'inactivePractitioner', }; @@ -99,7 +99,7 @@ describe('updatePractitionerUser', () => { Item: { barNumber: 'PT1234', name: 'Test Practitioner', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, section: 'inactivePractitioner', }, }), @@ -107,7 +107,7 @@ describe('updatePractitionerUser', () => { const updatedUser = { barNumber: 'PT1234', name: 'Test Practitioner', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, section: 'inactivePractitioner', }; @@ -130,7 +130,7 @@ describe('updatePractitionerUser', () => { Item: { barNumber: 'PT1234', name: 'Test Practitioner', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, section: 'inactivePractitioner', }, }), @@ -138,7 +138,7 @@ describe('updatePractitionerUser', () => { const updatedUser = { barNumber: 'PT1234', name: 'Test Practitioner', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, section: 'inactivePractitioner', }; @@ -156,7 +156,7 @@ describe('updatePractitionerUser - with a cognito response', () => { const updatedUser = { barNumber: 'PT1234', name: 'Test Practitioner', - role: User.ROLES.inactivePractitioner, + role: ROLES.inactivePractitioner, section: 'inactivePractitioner', }; diff --git a/shared/src/persistence/dynamo/users/updateUser.test.js b/shared/src/persistence/dynamo/users/updateUser.test.js index 43e3c3e2b90..69e4b0f11f8 100644 --- a/shared/src/persistence/dynamo/users/updateUser.test.js +++ b/shared/src/persistence/dynamo/users/updateUser.test.js @@ -1,14 +1,14 @@ const { applicationContext, } = require('../../../business/test/createTestApplicationContext'); +const { ROLES } = require('../../../business/entities/EntityConstants'); const { updateUser } = require('./updateUser'); -const { User } = require('../../../business/entities/User'); const mockUserId = '9b52c605-edba-41d7-b045-d5f992a499d3'; const mockUser = { name: 'Test User', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: mockUserId, }; diff --git a/shared/src/persistence/dynamo/workitems/setWorkItemAsRead.test.js b/shared/src/persistence/dynamo/workitems/setWorkItemAsRead.test.js index d82e26dd3df..f43a42629fe 100644 --- a/shared/src/persistence/dynamo/workitems/setWorkItemAsRead.test.js +++ b/shared/src/persistence/dynamo/workitems/setWorkItemAsRead.test.js @@ -13,7 +13,7 @@ describe('setWorkItemAsRead', () => { it('invokes the persistence layer with pk of {userId}|workItem and other expected params', async () => { await setWorkItemAsRead({ applicationContext, - userId: '123', + userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291', workItemId: 'abc', }); @@ -21,7 +21,7 @@ describe('setWorkItemAsRead', () => { applicationContext.getDocumentClient().update.mock.calls[0][0], ).toMatchObject({ Key: { - pk: 'user|123', + pk: 'user|15adf875-8c3c-4e94-91e9-a4c1bff51291', sk: 'work-item|abc', }, }); diff --git a/shared/src/persistence/dynamo/workitems/updateWorkItem.test.js b/shared/src/persistence/dynamo/workitems/updateWorkItem.test.js index a000b9c8a80..971216b6238 100644 --- a/shared/src/persistence/dynamo/workitems/updateWorkItem.test.js +++ b/shared/src/persistence/dynamo/workitems/updateWorkItem.test.js @@ -14,7 +14,7 @@ describe('updateWorkItem', () => { await updateWorkItem({ applicationContext, workItemToUpdate: { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', workItemId: '123', }, }); @@ -23,7 +23,7 @@ describe('updateWorkItem', () => { applicationContext.getDocumentClient().put.mock.calls[0][0], ).toMatchObject({ Item: { - assigneeId: 'bob', + assigneeId: '8b4cd447-6278-461b-b62b-d9e357eea62c', pk: 'work-item|123', sk: 'work-item|123', workItemId: '123', diff --git a/shared/src/persistence/dynamsoft/getScannerInterface.test.js b/shared/src/persistence/dynamsoft/getScannerInterface.test.js index 80853557b09..54508bff2e0 100644 --- a/shared/src/persistence/dynamsoft/getScannerInterface.test.js +++ b/shared/src/persistence/dynamsoft/getScannerInterface.test.js @@ -3,7 +3,7 @@ const { } = require('../../business/test/createTestApplicationContext'); const { getScannerInterface } = require('./getScannerInterface'); const { JSDOM } = require('jsdom'); -import { Scan } from '../../business/entities/Scan'; +import { SCAN_MODES } from '../../business/entities/EntityConstants'; describe('getScannerInterface', () => { const jsdom = new JSDOM(''); @@ -22,8 +22,6 @@ describe('getScannerInterface', () => { const mockOpenSource = jest.fn(); const mockRemoveAllImages = jest.fn(); - const { SCAN_MODES } = Scan; - applicationContext.getScannerResourceUri.mockReturnValue('abc'); const DWObject = { diff --git a/shared/src/persistence/elasticsearch/advancedDocumentSearch.js b/shared/src/persistence/elasticsearch/advancedDocumentSearch.js index 26a932509d7..d124b856767 100644 --- a/shared/src/persistence/elasticsearch/advancedDocumentSearch.js +++ b/shared/src/persistence/elasticsearch/advancedDocumentSearch.js @@ -25,6 +25,7 @@ exports.advancedDocumentSearch = async ({ 'documentId', 'documentTitle', 'documentType', + 'eventCode', 'filingDate', 'irsPractitioners', 'isSealed', @@ -94,13 +95,8 @@ exports.advancedDocumentSearch = async ({ if (docketNumber) { queryParams.push({ - simple_query_string: { - fields: [ - 'docketNumber.S', - 'docketNumberSuffix.S', - 'docketNumberWithSuffix.S', - ], - query: docketNumber, + match: { + 'docketNumber.S': { operator: 'and', query: docketNumber }, }, }); } diff --git a/shared/src/persistence/elasticsearch/advancedDocumentSearch.test.js b/shared/src/persistence/elasticsearch/advancedDocumentSearch.test.js index 60afe1095f5..f5825c8945f 100644 --- a/shared/src/persistence/elasticsearch/advancedDocumentSearch.test.js +++ b/shared/src/persistence/elasticsearch/advancedDocumentSearch.test.js @@ -193,13 +193,11 @@ describe('advancedDocumentSearch', () => { expect(searchStub.mock.calls[0][0].body.query.bool.must).toEqual([ ...orderQueryParams, { - simple_query_string: { - fields: [ - 'docketNumber.S', - 'docketNumberSuffix.S', - 'docketNumberWithSuffix.S', - ], - query: '101-20', + match: { + 'docketNumber.S': { + operator: 'and', + query: '101-20', + }, }, }, ]); diff --git a/shared/src/persistence/elasticsearch/bulkIndexRecords.js b/shared/src/persistence/elasticsearch/bulkIndexRecords.js index 2ba55c32bb5..75cf3bef5b8 100644 --- a/shared/src/persistence/elasticsearch/bulkIndexRecords.js +++ b/shared/src/persistence/elasticsearch/bulkIndexRecords.js @@ -25,8 +25,8 @@ exports.bulkIndexRecords = async ({ applicationContext, records }) => { }); const failedRecords = []; - if (response.body.errors) { - response.body.items.forEach((action, i) => { + if (response.errors) { + response.items.forEach((action, i) => { const operation = Object.keys(action)[0]; if (action[operation].error) { let record = body[i * 2 + 1]; diff --git a/shared/src/persistence/elasticsearch/bulkIndexRecords.test.js b/shared/src/persistence/elasticsearch/bulkIndexRecords.test.js new file mode 100644 index 00000000000..a4136455545 --- /dev/null +++ b/shared/src/persistence/elasticsearch/bulkIndexRecords.test.js @@ -0,0 +1,77 @@ +const { + applicationContext, +} = require('../../business/test/createTestApplicationContext'); +const { bulkIndexRecords } = require('./bulkIndexRecords'); + +describe('bulkIndexRecords', () => { + const newImageRecord = { + caseId: { S: '6f3d97f8-1bdd-4779-a150-c076d08ad8fd' }, + caseStatus: { S: 'New' }, + createdAt: { S: '2020-06-10T15:10:23.553Z' }, + docketNumber: { S: '105-19' }, + docketNumberWithSuffix: { S: '105-19' }, + entityName: { S: 'CaseMessage' }, + from: { S: 'Test Docketclerk' }, + fromSection: { S: 'docket' }, + fromUserId: { S: '1805d1ab-18d0-43ec-bafb-654e83405416' }, + gsi1pk: { S: 'message|2e30ecc2-3818-4855-ad3f-4a3ce8d29767' }, + message: { S: 'D' }, + messageId: { S: '2e30ecc2-3818-4855-ad3f-4a3ce8d29767' }, + pk: { S: 'case|6f3d97f8-1bdd-4779-a150-c076d08ad8fd' }, + sk: { S: 'message|2e30ecc2-3818-4855-ad3f-4a3ce8d29767' }, + subject: { S: 'S' }, + to: { S: 'Test Docketclerk' }, + toSection: { S: 'docket' }, + toUserId: { S: '1805d1ab-18d0-43ec-bafb-654e83405416' }, + }; + + const records = [ + { + dynamodb: { + NewImage: newImageRecord, + }, + }, + ]; + + it('returns no failed records if the bulk call is successful', async () => { + applicationContext.getSearchClient().bulk.mockReturnValue({ + errors: false, + items: [{}], + took: 100, + }); + + const result = await bulkIndexRecords({ + applicationContext, + records: records, + }); + expect(result.failedRecords).toEqual([]); + }); + + it('returns failed records if the bulk call is unsuccessful', async () => { + applicationContext.getSearchClient().bulk.mockReturnValue({ + errors: true, + items: [ + { + index: { + _index: 'efcms-message', + error: { + index: 'efcms-message', + index_uuid: 'aAsFqTI0Tc2W0LCWgPNrOA', + reason: 'document missing', + shard: '0', + type: 'document_missing_exception', + }, + status: 404, + }, + }, + ], + took: 100, + }); + + const result = await bulkIndexRecords({ + applicationContext, + records: records, + }); + expect(result.failedRecords).toEqual([newImageRecord]); + }); +}); diff --git a/shared/src/persistence/elasticsearch/getIndexMappingFields.js b/shared/src/persistence/elasticsearch/getIndexMappingFields.js index b7262bc646c..9cf34f11585 100644 --- a/shared/src/persistence/elasticsearch/getIndexMappingFields.js +++ b/shared/src/persistence/elasticsearch/getIndexMappingFields.js @@ -1,8 +1,8 @@ /** - * @param {object} args deconstructed arguments - * @param {object} args.applicationContext the application context - * @param {string} args.index the index we're querying - * @return {object} the mapping properties of the specified index + * @param {object} arguments deconstructed arguments + * @param {object} arguments.applicationContext the application context + * @param {string} arguments.index the index we're querying + * @returns {object} the mapping properties of the specified index */ exports.getIndexMappingFields = async ({ applicationContext, index }) => { const searchClient = applicationContext.getSearchClient(); diff --git a/shared/src/persistence/elasticsearch/getIndexMappingLimit.js b/shared/src/persistence/elasticsearch/getIndexMappingLimit.js index 79c2ec94869..7ae4e558bc8 100644 --- a/shared/src/persistence/elasticsearch/getIndexMappingLimit.js +++ b/shared/src/persistence/elasticsearch/getIndexMappingLimit.js @@ -2,7 +2,7 @@ * @param {object} params deconstructed arguments * @param {object} params.applicationContext the application context * @param {string} params.index the index we're querying - * @return {string} the limit for the specified index + * @returns {Promise} the limit for the specified index */ exports.getIndexMappingLimit = async ({ applicationContext, index }) => { const searchClient = applicationContext.getSearchClient(); diff --git a/shared/src/persistence/elasticsearch/getIndexNameForRecord.js b/shared/src/persistence/elasticsearch/getIndexNameForRecord.js index 616acfa8a28..bbf533757be 100644 --- a/shared/src/persistence/elasticsearch/getIndexNameForRecord.js +++ b/shared/src/persistence/elasticsearch/getIndexNameForRecord.js @@ -26,6 +26,10 @@ exports.getIndexNameForRecord = record => { index = 'efcms-document'; } else if (isRecordOfType(record, 'User')) { index = 'efcms-user'; + } else if (isRecordOfType(record, 'CaseMessage')) { + index = 'efcms-message'; + } else if (isRecordOfType(record, 'UserCase')) { + index = 'efcms-user-case'; } return index; diff --git a/shared/src/persistence/elasticsearch/getIndexNameForRecord.test.js b/shared/src/persistence/elasticsearch/getIndexNameForRecord.test.js index 8a69246ee06..a70a6e83735 100644 --- a/shared/src/persistence/elasticsearch/getIndexNameForRecord.test.js +++ b/shared/src/persistence/elasticsearch/getIndexNameForRecord.test.js @@ -5,6 +5,7 @@ describe('getIndexNameForRecord', () => { const record = {}; const result = getIndexNameForRecord(record); + expect(result).toEqual(null); }); @@ -16,6 +17,7 @@ describe('getIndexNameForRecord', () => { }; const result = getIndexNameForRecord(record); + expect(result).toEqual('efcms-case'); }); @@ -27,6 +29,7 @@ describe('getIndexNameForRecord', () => { }; const result = getIndexNameForRecord(record); + expect(result).toEqual('efcms-document'); }); @@ -38,6 +41,7 @@ describe('getIndexNameForRecord', () => { }; const result = getIndexNameForRecord(record); + expect(result).toEqual('efcms-user'); }); @@ -49,6 +53,7 @@ describe('getIndexNameForRecord', () => { }; const result = getIndexNameForRecord(record); + expect(result).toEqual('efcms-user'); }); @@ -60,6 +65,7 @@ describe('getIndexNameForRecord', () => { }; const result = getIndexNameForRecord(record); + expect(result).toEqual('efcms-user'); }); @@ -71,6 +77,31 @@ describe('getIndexNameForRecord', () => { }; const result = getIndexNameForRecord(record); + expect(result).toEqual('efcms-user'); }); + + it('returns efcms-message for CaseMessage records', () => { + const record = { + entityName: { + S: 'CaseMessage', + }, + }; + + const result = getIndexNameForRecord(record); + + expect(result).toEqual('efcms-message'); + }); + + it('returns efcms-user-case for UserCase records', () => { + const record = { + entityName: { + S: 'UserCase', + }, + }; + + const result = getIndexNameForRecord(record); + + expect(result).toEqual('efcms-user-case'); + }); }); diff --git a/shared/src/persistence/elasticsearch/getIndexedCasesForUser.js b/shared/src/persistence/elasticsearch/getIndexedCasesForUser.js new file mode 100644 index 00000000000..d0e235ec27a --- /dev/null +++ b/shared/src/persistence/elasticsearch/getIndexedCasesForUser.js @@ -0,0 +1,59 @@ +const { search } = require('./searchClient'); + +/** + * getIndexedCasesForUser + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.statuses case status to filter by + * @param {string} providers.userId the userId to filter cases by + * @returns {object} the case data + */ +exports.getIndexedCasesForUser = async ({ + applicationContext, + statuses, + userId, +}) => { + const { results } = await search({ + applicationContext, + searchParameters: { + body: { + _source: [ + 'docketNumber', + 'docketNumberWithSuffix', + 'caseCaption', + 'leadCaseId', + 'caseId', + 'createdAt', + 'status', + ], + query: { + bool: { + must: [ + { + match: { + 'pk.S': { operator: 'and', query: `user|${userId}` }, + }, + }, + { match: { 'sk.S': 'case|' } }, + { match: { 'gsi1pk.S': 'user-case|' } }, + { + bool: { + should: statuses.map(status => ({ + match: { + 'status.S': status, + }, + })), + }, + }, + ], + }, + }, + size: 5000, + }, + index: 'efcms-user-case', + }, + }); + + return results; +}; diff --git a/shared/src/persistence/elasticsearch/getIndexedCasesForUser.test.js b/shared/src/persistence/elasticsearch/getIndexedCasesForUser.test.js new file mode 100644 index 00000000000..86c2f88299e --- /dev/null +++ b/shared/src/persistence/elasticsearch/getIndexedCasesForUser.test.js @@ -0,0 +1,70 @@ +const { + applicationContext, +} = require('../../business/test/createTestApplicationContext'); +const { + CASE_STATUS_TYPES, +} = require('../../business/entities/EntityConstants'); +const { getIndexedCasesForUser } = require('./getIndexedCasesForUser'); + +describe('getIndexedCasesForUser', () => { + beforeEach(() => {}); + + it('should search for cases by the userId and statuses provided', async () => { + const mockUserId = '123'; + + await getIndexedCasesForUser({ + applicationContext, + statuses: [ + CASE_STATUS_TYPES.new, + CASE_STATUS_TYPES.jurisdictionRetained, + CASE_STATUS_TYPES.calendared, + ], + userId: mockUserId, + }); + + expect( + applicationContext.getSearchClient().search.mock.calls[0][0].body.query + .bool.must, + ).toMatchObject([ + { + match: { + 'pk.S': { + operator: 'and', + query: `user|${mockUserId}`, + }, + }, + }, + { + match: { + 'sk.S': 'case|', + }, + }, + { + match: { + 'gsi1pk.S': 'user-case|', + }, + }, + { + bool: { + should: [ + { + match: { + 'status.S': CASE_STATUS_TYPES.new, + }, + }, + { + match: { + 'status.S': 'Jurisdiction Retained', + }, + }, + { + match: { + 'status.S': 'Calendared', + }, + }, + ], + }, + }, + ]); + }); +}); diff --git a/shared/src/persistence/elasticsearch/messages/getSectionInboxMessages.js b/shared/src/persistence/elasticsearch/messages/getSectionInboxMessages.js new file mode 100644 index 00000000000..6fe8c0770d2 --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getSectionInboxMessages.js @@ -0,0 +1,26 @@ +const { search } = require('../searchClient'); + +exports.getSectionInboxMessages = async ({ applicationContext, section }) => { + const query = { + body: { + query: { + bool: { + must: { + match: { + 'toSection.S': { operator: 'and', query: section }, + }, + }, + }, + }, + size: 5000, + }, + index: 'efcms-message', + }; + + const { results } = await search({ + applicationContext, + searchParameters: query, + }); + + return results; +}; diff --git a/shared/src/persistence/elasticsearch/messages/getSectionInboxMessages.test.js b/shared/src/persistence/elasticsearch/messages/getSectionInboxMessages.test.js new file mode 100644 index 00000000000..b6f8bca6067 --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getSectionInboxMessages.test.js @@ -0,0 +1,20 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { getSectionInboxMessages } = require('./getSectionInboxMessages'); +jest.mock('../searchClient'); +const { search } = require('../searchClient'); + +describe('getSectionInboxMessages', () => { + it('returns results from the search client', async () => { + search.mockReturnValue({ results: ['some', 'matches'], total: 0 }); + + const results = await getSectionInboxMessages({ + applicationContext, + section: 'petitions', + }); + + expect(search).toHaveBeenCalledTimes(1); + expect(results).toMatchObject(['some', 'matches']); + }); +}); diff --git a/shared/src/persistence/elasticsearch/messages/getSectionOutboxMessages.js b/shared/src/persistence/elasticsearch/messages/getSectionOutboxMessages.js new file mode 100644 index 00000000000..6655fb2212e --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getSectionOutboxMessages.js @@ -0,0 +1,26 @@ +const { search } = require('../searchClient'); + +exports.getSectionOutboxMessages = async ({ applicationContext, section }) => { + const query = { + body: { + query: { + bool: { + must: { + match: { + 'fromSection.S': { operator: 'and', query: section }, + }, + }, + }, + }, + size: 5000, + }, + index: 'efcms-message', + }; + + const { results } = await search({ + applicationContext, + searchParameters: query, + }); + + return results; +}; diff --git a/shared/src/persistence/elasticsearch/messages/getSectionOutboxMessages.test.js b/shared/src/persistence/elasticsearch/messages/getSectionOutboxMessages.test.js new file mode 100644 index 00000000000..3204b439b8d --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getSectionOutboxMessages.test.js @@ -0,0 +1,20 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { getSectionOutboxMessages } = require('./getSectionOutboxMessages'); +jest.mock('../searchClient'); +const { search } = require('../searchClient'); + +describe('getSectionOutboxMessages', () => { + it('returns results from the search client', async () => { + search.mockReturnValue({ results: ['some', 'matches'], total: 0 }); + + const results = await getSectionOutboxMessages({ + applicationContext, + section: 'petitions', + }); + + expect(search).toHaveBeenCalledTimes(1); + expect(results).toMatchObject(['some', 'matches']); + }); +}); diff --git a/shared/src/persistence/elasticsearch/messages/getUserInboxMessages.js b/shared/src/persistence/elasticsearch/messages/getUserInboxMessages.js new file mode 100644 index 00000000000..1d9e0d925de --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getUserInboxMessages.js @@ -0,0 +1,26 @@ +const { search } = require('../searchClient'); + +exports.getUserInboxMessages = async ({ applicationContext, userId }) => { + const query = { + body: { + query: { + bool: { + must: { + match: { + 'toUserId.S': { operator: 'and', query: userId }, + }, + }, + }, + }, + size: 5000, + }, + index: 'efcms-message', + }; + + const { results } = await search({ + applicationContext, + searchParameters: query, + }); + + return results; +}; diff --git a/shared/src/persistence/elasticsearch/messages/getUserInboxMessages.test.js b/shared/src/persistence/elasticsearch/messages/getUserInboxMessages.test.js new file mode 100644 index 00000000000..ef8656addc1 --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getUserInboxMessages.test.js @@ -0,0 +1,20 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { getUserInboxMessages } = require('./getUserInboxMessages'); +jest.mock('../searchClient'); +const { search } = require('../searchClient'); + +describe('getUserInboxMessages', () => { + it('returns results from the search client', async () => { + search.mockReturnValue({ results: ['some', 'matches'], total: 0 }); + + const results = await getUserInboxMessages({ + applicationContext, + userId: 'f5d68c53-af31-484d-803b-da22c4d03357', + }); + + expect(search).toHaveBeenCalledTimes(1); + expect(results).toMatchObject(['some', 'matches']); + }); +}); diff --git a/shared/src/persistence/elasticsearch/messages/getUserOutboxMessages.js b/shared/src/persistence/elasticsearch/messages/getUserOutboxMessages.js new file mode 100644 index 00000000000..d11d9b7bdd4 --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getUserOutboxMessages.js @@ -0,0 +1,26 @@ +const { search } = require('../searchClient'); + +exports.getUserOutboxMessages = async ({ applicationContext, userId }) => { + const query = { + body: { + query: { + bool: { + must: { + match: { + 'fromUserId.S': { operator: 'and', query: userId }, + }, + }, + }, + }, + size: 5000, + }, + index: 'efcms-message', + }; + + const { results } = await search({ + applicationContext, + searchParameters: query, + }); + + return results; +}; diff --git a/shared/src/persistence/elasticsearch/messages/getUserOutboxMessages.test.js b/shared/src/persistence/elasticsearch/messages/getUserOutboxMessages.test.js new file mode 100644 index 00000000000..34d004f6280 --- /dev/null +++ b/shared/src/persistence/elasticsearch/messages/getUserOutboxMessages.test.js @@ -0,0 +1,20 @@ +const { + applicationContext, +} = require('../../../business/test/createTestApplicationContext'); +const { getUserOutboxMessages } = require('./getUserOutboxMessages'); +jest.mock('../searchClient'); +const { search } = require('../searchClient'); + +describe('getUserOutboxMessages', () => { + it('returns results from the search client', async () => { + search.mockReturnValue({ results: ['some', 'matches'], total: 0 }); + + const results = await getUserOutboxMessages({ + applicationContext, + userId: '318de3b3-1625-4638-98a3-c67ab1b17be7', + }); + + expect(search).toHaveBeenCalledTimes(1); + expect(results).toMatchObject(['some', 'matches']); + }); +}); diff --git a/shared/src/persistence/s3/getUploadPolicy.js b/shared/src/persistence/s3/getUploadPolicy.js index ed8cc569390..6f1a06a2206 100644 --- a/shared/src/persistence/s3/getUploadPolicy.js +++ b/shared/src/persistence/s3/getUploadPolicy.js @@ -1,5 +1,6 @@ -exports.MAX_FILE_SIZE_MB = 250; // megabytes -exports.MAX_FILE_SIZE_BYTES = exports.MAX_FILE_SIZE_MB * 1024 * 1024; // bytes -> megabytes +const { + MAX_FILE_SIZE_BYTES, +} = require('../../business/entities/EntityConstants'); /** * getUploadPolicy @@ -16,7 +17,7 @@ exports.getUploadPolicy = ({ applicationContext, documentId }) => Conditions: [ ['starts-with', '$key', documentId], ['starts-with', '$Content-Type', ''], - ['content-length-range', 0, exports.MAX_FILE_SIZE_BYTES], + ['content-length-range', 0, MAX_FILE_SIZE_BYTES], ], }, (err, data) => { diff --git a/shared/src/proxies/caseStatistics/addDeficiencyStatisticProxy.js b/shared/src/proxies/caseStatistics/addDeficiencyStatisticProxy.js new file mode 100644 index 00000000000..a74925f9915 --- /dev/null +++ b/shared/src/proxies/caseStatistics/addDeficiencyStatisticProxy.js @@ -0,0 +1,42 @@ +const { post } = require('../requests'); + +/** + * addDeficiencyStatisticInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to update statistics + * @param {number} providers.determinationDeficiencyAmount deficiency amount determined by the court + * @param {number} providers.determinationTotalPenalties total penalties amount determined by the court + * @param {number} providers.irsDeficiencyAmount deficiency amount from the IRS + * @param {number} providers.irsTotalPenalties total penalties amount from the IRS + * @param {string} providers.lastDateOfPeriod last date of the period for the statistic + * @param {number} providers.year year for the statistic + * @param {string} providers.yearOrPeriod whether the statistic is for a year or period + * @returns {Promise<*>} the promise of the api call + */ +exports.addDeficiencyStatisticInteractor = ({ + applicationContext, + caseId, + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + year, + yearOrPeriod, +}) => { + return post({ + applicationContext, + body: { + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + year, + yearOrPeriod, + }, + endpoint: `/case-meta/${caseId}/statistics`, + }); +}; diff --git a/shared/src/proxies/caseStatistics/deleteDeficiencyStatisticProxy.js b/shared/src/proxies/caseStatistics/deleteDeficiencyStatisticProxy.js new file mode 100644 index 00000000000..91b80c4f366 --- /dev/null +++ b/shared/src/proxies/caseStatistics/deleteDeficiencyStatisticProxy.js @@ -0,0 +1,21 @@ +const { remove } = require('../requests'); + +/** + * deleteDeficiencyStatisticInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to update statistics + * @param {string} providers.statisticId the id of the statistic to update + * @returns {Promise<*>} the promise of the api call + */ +exports.deleteDeficiencyStatisticInteractor = ({ + applicationContext, + caseId, + statisticId, +}) => { + return remove({ + applicationContext, + endpoint: `/case-meta/${caseId}/statistics/${statisticId}`, + }); +}; diff --git a/shared/src/proxies/caseStatistics/updateDeficiencyStatisticProxy.js b/shared/src/proxies/caseStatistics/updateDeficiencyStatisticProxy.js new file mode 100644 index 00000000000..1f2e6048d68 --- /dev/null +++ b/shared/src/proxies/caseStatistics/updateDeficiencyStatisticProxy.js @@ -0,0 +1,44 @@ +const { put } = require('../requests'); + +/** + * updateDeficiencyStatisticInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to update statistics + * @param {number} providers.determinationDeficiencyAmount deficiency amount determined by the court + * @param {number} providers.determinationTotalPenalties total penalties amount determined by the court + * @param {number} providers.irsDeficiencyAmount deficiency amount from the IRS + * @param {number} providers.irsTotalPenalties total penalties amount from the IRS + * @param {string} providers.lastDateOfPeriod last date of the period for the statistic + * @param {string} providers.statisticId the id of the statistic to update + * @param {number} providers.year year for the statistic + * @param {string} providers.yearOrPeriod whether the statistic is for a year or period + * @returns {Promise<*>} the promise of the api call + */ +exports.updateDeficiencyStatisticInteractor = ({ + applicationContext, + caseId, + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + statisticId, + year, + yearOrPeriod, +}) => { + return put({ + applicationContext, + body: { + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + year, + yearOrPeriod, + }, + endpoint: `/case-meta/${caseId}/statistics/${statisticId}`, + }); +}; diff --git a/shared/src/proxies/caseStatistics/updateOtherStatisticsProxy.js b/shared/src/proxies/caseStatistics/updateOtherStatisticsProxy.js new file mode 100644 index 00000000000..5192ba519af --- /dev/null +++ b/shared/src/proxies/caseStatistics/updateOtherStatisticsProxy.js @@ -0,0 +1,24 @@ +const { post } = require('../requests'); + +/** + * updateOtherStatisticsInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case to update statistics + * @param {number} providers.damages damages statistic to add to the case + * @param {number} providers.litigationCosts litigation costs statistic to add to the case + * @returns {Promise<*>} the promise of the api call + */ +exports.updateOtherStatisticsInteractor = ({ + applicationContext, + caseId, + damages, + litigationCosts, +}) => { + return post({ + applicationContext, + body: { damages, litigationCosts }, + endpoint: `/case-meta/${caseId}/other-statistics`, + }); +}; diff --git a/shared/src/proxies/correspondence/deleteCorrespondenceDocumentProxy.js b/shared/src/proxies/correspondence/deleteCorrespondenceDocumentProxy.js new file mode 100644 index 00000000000..ffac1d99285 --- /dev/null +++ b/shared/src/proxies/correspondence/deleteCorrespondenceDocumentProxy.js @@ -0,0 +1,21 @@ +const { remove } = require('../requests'); + +/** + * deleteCorrespondenceDocumentInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {object} providers.caseId the id of the case that contains the document to delete + * @param {string} providers.documentId the id of the correspondence document + * @returns {Promise<*>} the promise of the api call + */ +exports.deleteCorrespondenceDocumentInteractor = ({ + applicationContext, + caseId, + documentId, +}) => { + return remove({ + applicationContext, + endpoint: `/case-documents/${caseId}/correspondence/${documentId}`, + }); +}; diff --git a/shared/src/proxies/correspondence/fileCorrespondenceDocumentInteractor.js b/shared/src/proxies/correspondence/fileCorrespondenceDocumentProxy.js similarity index 100% rename from shared/src/proxies/correspondence/fileCorrespondenceDocumentInteractor.js rename to shared/src/proxies/correspondence/fileCorrespondenceDocumentProxy.js diff --git a/shared/src/proxies/correspondence/updateCorrespondenceDocumentProxy.js b/shared/src/proxies/correspondence/updateCorrespondenceDocumentProxy.js index 6951cd3e351..c2794d5f6f6 100644 --- a/shared/src/proxies/correspondence/updateCorrespondenceDocumentProxy.js +++ b/shared/src/proxies/correspondence/updateCorrespondenceDocumentProxy.js @@ -11,15 +11,16 @@ const { put } = require('../requests'); */ exports.updateCorrespondenceDocumentInteractor = ({ applicationContext, - documentIdToEdit, + documentId, documentMetadata, }) => { const { caseId } = documentMetadata; + return put({ applicationContext, body: { documentMetadata, }, - endpoint: `/case-documents/${caseId}/correspondence/${documentIdToEdit}`, + endpoint: `/case-documents/${caseId}/correspondence/${documentId}`, }); }; diff --git a/shared/src/proxies/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlProxy.js b/shared/src/proxies/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlProxy.js index cda5976b036..616ac494bbe 100644 --- a/shared/src/proxies/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlProxy.js +++ b/shared/src/proxies/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlProxy.js @@ -5,20 +5,26 @@ const { post } = require('../requests'); * * @param {object} providers the providers object * @param {object} providers.applicationContext the application context - * @param {string} providers.docketNumberWithSuffix the docket number of the case with the suffix - * @param {string} providers.htmlString the htmlString for the pdf content + * @param {string} providers.caseId the case id where the order is generated + * @param {string} providers.contentHtml the html string for the pdf content + * @param {string} providers.documentTitle the title of the document + * @param {string} providers.signatureText (optional) text to be used as the signatory of the document * @returns {Promise<*>} the promise of the api call */ exports.createCourtIssuedOrderPdfFromHtmlInteractor = ({ applicationContext, - docketNumberWithSuffix, - htmlString, + caseId, + contentHtml, + documentTitle, + signatureText, }) => { return post({ applicationContext, body: { - docketNumberWithSuffix, - htmlString, + caseId, + contentHtml, + documentTitle, + signatureText, }, endpoint: '/api/court-issued-order', }); diff --git a/shared/src/proxies/getConsolidatedCasesByUserProxy.js b/shared/src/proxies/getClosedCasesProxy.js similarity index 58% rename from shared/src/proxies/getConsolidatedCasesByUserProxy.js rename to shared/src/proxies/getClosedCasesProxy.js index 475d8f63f37..54021128a57 100644 --- a/shared/src/proxies/getConsolidatedCasesByUserProxy.js +++ b/shared/src/proxies/getClosedCasesProxy.js @@ -1,15 +1,15 @@ const { get } = require('./requests'); /** + * getClosedCasesInteractor * * @param {object} providers the providers object * @param {object} providers.applicationContext the application context * @returns {Promise<*>} the promise of the api call */ -exports.getConsolidatedCasesByUserInteractor = ({ applicationContext }) => { - const user = applicationContext.getCurrentUser(); +exports.getClosedCasesInteractor = ({ applicationContext }) => { return get({ applicationContext, - endpoint: `/users/${user.userId}/cases-with-consolidation`, + endpoint: '/cases/closed', }); }; diff --git a/shared/src/proxies/getOpenConsolidatedCasesProxy.js b/shared/src/proxies/getOpenConsolidatedCasesProxy.js new file mode 100644 index 00000000000..6e99dd27178 --- /dev/null +++ b/shared/src/proxies/getOpenConsolidatedCasesProxy.js @@ -0,0 +1,15 @@ +const { get } = require('./requests'); + +/** + * getOpenConsolidatedCasesInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @returns {Promise<*>} the promise of the api call + */ +exports.getOpenConsolidatedCasesInteractor = ({ applicationContext }) => { + return get({ + applicationContext, + endpoint: '/cases/open', + }); +}; diff --git a/shared/src/proxies/messages/createCaseMessageProxy.js b/shared/src/proxies/messages/createCaseMessageProxy.js new file mode 100644 index 00000000000..507ba6c1465 --- /dev/null +++ b/shared/src/proxies/messages/createCaseMessageProxy.js @@ -0,0 +1,36 @@ +const { post } = require('../requests'); + +/** + * createCaseMessageInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.caseId the id of the case + * @param {string} providers.message the message text + * @param {string} providers.subject the message subject + * @param {string} providers.toSection the section of the user receiving the message + * @param {string} providers.toUserId the user id of the user receiving the message + * @returns {Promise<*>} the promise of the api call + */ +exports.createCaseMessageInteractor = ({ + applicationContext, + attachments, + caseId, + message, + subject, + toSection, + toUserId, +}) => { + return post({ + applicationContext, + body: { + attachments, + caseId, + message, + subject, + toSection, + toUserId, + }, + endpoint: '/messages/', + }); +}; diff --git a/shared/src/proxies/messages/getCaseMessageProxy.js b/shared/src/proxies/messages/getCaseMessageProxy.js new file mode 100644 index 00000000000..ecc64beff70 --- /dev/null +++ b/shared/src/proxies/messages/getCaseMessageProxy.js @@ -0,0 +1,16 @@ +const { get } = require('../requests'); + +/** + * getCaseMessageInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.messageId the message id + * @returns {Promise<*>} the promise of the api call + */ +exports.getCaseMessageInteractor = ({ applicationContext, messageId }) => { + return get({ + applicationContext, + endpoint: `/messages/${messageId}`, + }); +}; diff --git a/shared/src/proxies/messages/getInboxCaseMessagesForSectionProxy.js b/shared/src/proxies/messages/getInboxCaseMessagesForSectionProxy.js new file mode 100644 index 00000000000..04489ee0876 --- /dev/null +++ b/shared/src/proxies/messages/getInboxCaseMessagesForSectionProxy.js @@ -0,0 +1,19 @@ +const { get } = require('../requests'); + +/** + * getInboxCaseMessagesForSectionInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.section the section + * @returns {Promise<*>} the promise of the api call + */ +exports.getInboxCaseMessagesForSectionInteractor = ({ + applicationContext, + section, +}) => { + return get({ + applicationContext, + endpoint: `/messages/inbox/section/${section}`, + }); +}; diff --git a/shared/src/proxies/messages/getInboxCaseMessagesForUserProxy.js b/shared/src/proxies/messages/getInboxCaseMessagesForUserProxy.js new file mode 100644 index 00000000000..4f0fcf02708 --- /dev/null +++ b/shared/src/proxies/messages/getInboxCaseMessagesForUserProxy.js @@ -0,0 +1,19 @@ +const { get } = require('../requests'); + +/** + * getInboxCaseMessagesForUserInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.userId the user id + * @returns {Promise<*>} the promise of the api call + */ +exports.getInboxCaseMessagesForUserInteractor = ({ + applicationContext, + userId, +}) => { + return get({ + applicationContext, + endpoint: `/messages/inbox/${userId}`, + }); +}; diff --git a/shared/src/proxies/messages/getOutboxCaseMessagesForSectionProxy.js b/shared/src/proxies/messages/getOutboxCaseMessagesForSectionProxy.js new file mode 100644 index 00000000000..7573f7a4a9d --- /dev/null +++ b/shared/src/proxies/messages/getOutboxCaseMessagesForSectionProxy.js @@ -0,0 +1,19 @@ +const { get } = require('../requests'); + +/** + * getOutboxCaseMessagesForSectionInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.section the section + * @returns {Promise<*>} the promise of the api call + */ +exports.getOutboxCaseMessagesForSectionInteractor = ({ + applicationContext, + section, +}) => { + return get({ + applicationContext, + endpoint: `/messages/outbox/section/${section}`, + }); +}; diff --git a/shared/src/proxies/messages/getOutboxCaseMessagesForUserProxy.js b/shared/src/proxies/messages/getOutboxCaseMessagesForUserProxy.js new file mode 100644 index 00000000000..e75d75c47c3 --- /dev/null +++ b/shared/src/proxies/messages/getOutboxCaseMessagesForUserProxy.js @@ -0,0 +1,19 @@ +const { get } = require('../requests'); + +/** + * getOutboxCaseMessagesForUserInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.userId the user id + * @returns {Promise<*>} the promise of the api call + */ +exports.getOutboxCaseMessagesForUserInteractor = ({ + applicationContext, + userId, +}) => { + return get({ + applicationContext, + endpoint: `/messages/outbox/${userId}`, + }); +}; diff --git a/shared/src/proxies/public/getCaseForPublicDocketNumberSearchProxy.js b/shared/src/proxies/public/getCaseForPublicDocketNumberSearchProxy.js new file mode 100644 index 00000000000..2f1192dd80c --- /dev/null +++ b/shared/src/proxies/public/getCaseForPublicDocketNumberSearchProxy.js @@ -0,0 +1,19 @@ +const { get } = require('../requests'); + +/** + * getCaseForPublicDocketSearchInteractor + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {string} providers.docketNumber the docket number to search by + * @returns {Promise<*>} the promise of the api call + */ +exports.getCaseForPublicDocketSearchInteractor = ({ + applicationContext, + docketNumber, +}) => { + return get({ + applicationContext, + endpoint: `/public-api/docket-number-search/${docketNumber}`, + }); +}; diff --git a/shared/src/proxies/public/getTodaysOpinionsProxy.js b/shared/src/proxies/public/getTodaysOpinionsProxy.js new file mode 100644 index 00000000000..a7e769f5438 --- /dev/null +++ b/shared/src/proxies/public/getTodaysOpinionsProxy.js @@ -0,0 +1,15 @@ +const { get } = require('../requests'); + +/** + * getTodaysOpinionsProxy + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @returns {Promise<*>} the promise of the api call + */ +exports.getTodaysOpinionsInteractor = ({ applicationContext }) => { + return get({ + applicationContext, + endpoint: '/public-api/todays-opinions', + }); +}; diff --git a/shared/src/test/mockCase.js b/shared/src/test/mockCase.js index b66e47cfe6c..ad9a59e9f2c 100644 --- a/shared/src/test/mockCase.js +++ b/shared/src/test/mockCase.js @@ -15,6 +15,8 @@ exports.MOCK_CASE = { state: 'TN', title: 'Executor', }, + correspondence: [], + createdAt: '2018-03-01T21:40:46.415Z', docketNumber: '101-18', docketNumberWithSuffix: '101-18', docketRecord: [ diff --git a/shared/src/test/mockDocuments.js b/shared/src/test/mockDocuments.js index 6b4e7f66316..d55aab8b5a8 100644 --- a/shared/src/test/mockDocuments.js +++ b/shared/src/test/mockDocuments.js @@ -7,7 +7,7 @@ exports.MOCK_DOCUMENTS = [ documentType: 'Petition', eventCode: 'P', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -18,7 +18,7 @@ exports.MOCK_DOCUMENTS = [ documentType: 'Statement of Taxpayer Identification', eventCode: 'STIN', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -29,7 +29,7 @@ exports.MOCK_DOCUMENTS = [ documentType: 'Answer', eventCode: 'A', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -40,7 +40,7 @@ exports.MOCK_DOCUMENTS = [ documentType: 'Proposed Stipulated Decision', eventCode: 'PSDE', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, ]; diff --git a/shared/src/tools/court-issued-event-codes.csv b/shared/src/tools/court-issued-event-codes.csv index 6d121e572bf..83de0170276 100644 --- a/shared/src/tools/court-issued-event-codes.csv +++ b/shared/src/tools/court-issued-event-codes.csv @@ -1,17 +1,17 @@ -CODE,DOCKET ENTRY LANGUAGE,Display in Dropdown ,Nonstandard type ,What is this order for? ,Judge's name ,Date,Docket number(s),Trial location (dropdown) ,Concatenated Docket Entry ,Served Stamp -O,ORDER *ANYTHING*,O - Order ,Type A ,[Anything],,,,,[Anything],Served OR Entered and Served -OAJ,ORDER that case is assigned to *JUDGE* . *ANYTHING*,OAJ - Order that case is assigned ,Type B,[Anything],[Judge Name],,,,Order [Judge name] [Anything],Served -OAL,"ORDER that the letter ""L"" is added to the Dkt. No. *ANYTHING*","OAL - Order that the letter ""L"" is added to Docket number ",Type C,,,,[Docket number],,"Order that the letter ""L"" is added to Docket number [Anything]",Served -OAP,ORDER for Amended Petition on *DATE*. *ANY*,OAP - Order for Amended Petition ,Type D,[Anything],,[Date],,,Order for Amended Petition on [Date] [Anything],Served -OAPF,ORDER for Amended Petition and Filing Fee on *DATE*. *ANY*,OAPF - Order for Amended Petition and Filing Fee ,Type D,[Anything],,[Date],,,Order for Amended Petition and Filing Fee on [Date] [Anything],Served -OAR,"ORDER that the letter ""R"" is added to the Dkt. No. *ANYTHING*","OAR - Order that the letter ""R"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""R"" is added to the Docket number [Docket number]",Served -OAS,"ORDER that the letter ""S"" is added to the Dkt. No. *ANYTHING*","OAS - Order that the letter ""S"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""S"" is added to the Docket number [Docket number]",Served -OASL,"ORDER that the letters ""SL"" are added to the Dkt. No. *ANY*","OASL - Order that the letters ""SL"" are added to the Docket number ",Type C,,,,[Docket number],,"Order that the letters ""SL"" are added to the Docket number [Docket number]",Served -OAW,"ORDER that the letter ""W"" is added to the Dkt. No. *ANYTHING*","OAW - Order that the letter ""W"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""W"" is added to the Docket number [Docket number]",Served -OAX,"ORDER that the letter ""X"" is added to the Dkt. No. *ANYTHING*","OAX - Order that the letter ""X"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""X"" is added to the Docket number [Docket number]",Served +CODE,DOCKET ENTRY LANGUAGE,Display in Dropdown ,Nonstandard type ,What is this order for? ,Judge's name ,Date,Docket number(s),Trial location (dropdown) ,Concatenated Docket Entry ,Served Stamp +O,ORDER *ANYTHING*,O - Order ,Type A ,[Anything],,,,,[Anything],Served OR Entered and Served +OAJ,ORDER that case is assigned to *JUDGE* . *ANYTHING*,OAJ - Order that case is assigned ,Type B,[Anything],[Judge Name],,,,Order [Judge name] [Anything],Served +OAL,"ORDER that the letter ""L"" is added to the Dkt. No. *ANYTHING*","OAL - Order that the letter ""L"" is added to Docket number ",Type C,,,,[Docket number],,"Order that the letter ""L"" is added to Docket number [Anything]",Served +OAP,ORDER for Amended Petition on *DATE*. *ANY*,OAP - Order for Amended Petition ,Type D,[Anything],,[Date],,,Order for Amended Petition on [Date] [Anything],Served +OAPF,ORDER for Amended Petition and Filing Fee on *DATE*. *ANY*,OAPF - Order for Amended Petition and Filing Fee ,Type D,[Anything],,[Date],,,Order for Amended Petition and Filing Fee on [Date] [Anything],Served +OAR,"ORDER that the letter ""R"" is added to the Dkt. No. *ANYTHING*","OAR - Order that the letter ""R"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""R"" is added to the Docket number [Docket number]",Served +OAS,"ORDER that the letter ""S"" is added to the Dkt. No. *ANYTHING*","OAS - Order that the letter ""S"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""S"" is added to the Docket number [Docket number]",Served +OASL,"ORDER that the letters ""SL"" are added to the Dkt. No. *ANY*","OASL - Order that the letters ""SL"" are added to the Docket number ",Type C,,,,[Docket number],,"Order that the letters ""SL"" are added to the Docket number [Docket number]",Served +OAW,"ORDER that the letter ""W"" is added to the Dkt. No. *ANYTHING*","OAW - Order that the letter ""W"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""W"" is added to the Docket number [Docket number]",Served +OAX,"ORDER that the letter ""X"" is added to the Dkt. No. *ANYTHING*","OAX - Order that the letter ""X"" is added to the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""X"" is added to the Docket number [Docket number]",Served OCA,ORDER that caption of case is amended. *ANYTHING*,OCA - Order that caption of case is amended ,Type A ,[Anything],,,,,Order that caption of case is amended [Anything],Served -OD,"ORDER OF DISMISSAL Entered,, *JUDGE*. *ANYTHING*","OD - Order of Dismissal Entered, ",Type B,[Anything],[Judge name],,,,"Order of Dismissal Entered, [Judge Name] [Anything]",Entered and Served -ODD,"ORDER OF DISMISSAL AND DECISION Entered,, *JUDGE*. *ANYTHING*","ODD - Order of Dismissal and Decision Entered, ",Type B,[Anything],[Judge name],,,,"Order of Dismissal and Decision Entered, [Judge Name] [Anything]",Entered and Served +OD,"ORDER OF DISMISSAL Entered,, *JUDGE*. *ANYTHING*","OD - Order of Dismissal Entered",Type B,[Anything],[Judge name],,,,"Order of Dismissal Entered, [Judge Name] [Anything]",Entered and Served +ODD,"ORDER OF DISMISSAL AND DECISION Entered,, *JUDGE*. *ANYTHING*","ODD - Order of Dismissal and Decision Entered, ",Type B,[Anything],[Judge name],,,,"Order of Dismissal and Decision Entered, [Judge Name] [Anything]",Entered and Served ODL,"ORDER that the letter ""L"" is deleted from the Dkt. No. *ANY*","ODL - Order that the letter ""L"" is deleted from the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""L"" is deleted from the Docket number [Docket number]",Served ODP,"ORDER that the letter ""P"" is deleted from the Dkt. No. *ANYTHING*","ODP - Order that the letter ""P"" is deleted from the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""P"" is deleted from the Docket number [Docket number]",Served ODR,"ORDER that the letter ""R"" is deleted from the Dkt. No. *ANY*","ODR - Order that the letter ""R"" is deleted from the Docket number ",Type C,,,,[Docket number],,"Order that the letter ""R"" is deleted from the Docket number [Docket number]",Served @@ -35,10 +35,10 @@ OSC,ORDER *ANYTHING*,OSC - Order ,Type A ,[Anything],,,,,Order [Anything],Served OSCP,"ORDER petr(s) by *DATE* show cause why ""S"" should not be removed *ANYTHING*","OSCP - Order petr(s) to show cause why ""S"" should not be removed ",Type D,[Anything],,[Date],,,"OSCP - Order petr(s) by [Date] to show cause why ""S"" should not be removed [Anything]",Served OST,ORDER of Service of Transcript (Bench Opinion) *ANYTHING*,OST - Order of Service of Transcript (Bench Opinion),Type A ,[Anything],,,,,Order of Service of Transcript (Bench Opinion) [Anything],Served OSUB,ORDER that case is submitted to *JUDGE*. *ANYTHING*,OSUB - Order that case is submitted,Type B,[Anything],[Judge name],,,,Order that case is submitted to [Judge Name] [Anything],Served -DEC,"DECISION Entered,, *JUDGE* *ANYTHING*","DEC - Decision Entered, ",Type B,[Anything],[Judge name],,,,"Decision Entered, [Judge Name] [Anything]",Entered and Served -OAD,"ORDER AND DECISION Entered,, *ANY* *JUDGE*. *ANYTHING*","OAD - Order and Decision Entered,",Type B,[Anything],[Judge name],,,,"Order and Decision Entered, [Judge Name] [Anything]",Entered and Served -ODJ,"ORDER OF DISMISSAL FOR LACK OF JURISDICTION Entered,, *JUDGE*. *ANYTHING*","ODJ - Order of Dismissal for Lack of Jurisdiction Entered,",Type B,[Anything],[Judge name],,,,"Order of Dismissal for Lack of Jurisdiction Entered, [Judge Name] [Anything]",Entered and Served -SDEC,"STIPULATED DECISION Entered, *JUDGE*. *ANYTHING*","SDEC - Stipulated Decision Entered,",Type B,[Anything],[Judge name],,,,"Stipulated Decision Entered, [Judge Name] [Anything]",Entered and Served +DEC,"DECISION Entered,, *JUDGE* *ANYTHING*","DEC - Decision Entered, ",Type B,[Anything],[Judge name],,,,"Decision Entered, [Judge Name] [Anything]",Entered and Served +OAD,"ORDER AND DECISION Entered,, *ANY* *JUDGE*. *ANYTHING*","OAD - Order and Decision Entered,",Type B,[Anything],[Judge name],,,,"Order and Decision Entered, [Judge Name] [Anything]",Entered and Served +ODJ,"ORDER OF DISMISSAL FOR LACK OF JURISDICTION Entered,, *JUDGE*. *ANYTHING*","ODJ - Order of Dismissal for Lack of Jurisdiction Entered,",Type B,[Anything],[Judge name],,,,"Order of Dismissal for Lack of Jurisdiction Entered, [Judge Name] [Anything]",Entered and Served +SDEC,"STIPULATED DECISION Entered, *JUDGE*. *ANYTHING*","SDEC - Stipulated Decision Entered,",Type B,[Anything],[Judge name],,,,"Stipulated Decision Entered, [Judge Name] [Anything]",Entered and Served MOP ,Memorandum Opinion [judge],MOP - Memorandum Opinion ,Type B,[Anything],[Judge name],,,,Memorandum Opinion [judge], NOT ,Notice [Anything],NOT - Notice ,Type A ,[Anything],,,,,[Anything], SOP ,Summary Opinion [judge],Summary Opinion ,Type B,[Anything],[Judge name],,,,Summary Opinion [judge], @@ -54,4 +54,4 @@ TCOP ,T.C. Opinion [judge] [anything],TCOP - T.C. Opinion,Type B,[Anything],[Jud RTRA ,Revised Transcript of [anything],RTRA - Revised Transcript ,Type A,[Anything],,,,,Revised Transcript of [anything], TRAN,Transcript of [anything],TRAN - Transcript ,Type H,[Anything],,[Date],,,Transcript of [anything] on [date], SPTO,Standing Pre-Trial Order ,SPTO - Standing Pre-Trial Order ,HOLD hold (dependent on if we get to automated logic) ,,,,,,Standing Pre-Trial Order , -MISC,,MISC - Miscellaneous ,Type A,[Anything],,,,,, \ No newline at end of file +MISC,,MISC - Miscellaneous ,Type A,[Anything],,,,,, diff --git a/shared/src/tools/courtIssuedEventCodes.json b/shared/src/tools/courtIssuedEventCodes.json index dd734654ffa..6d0f9f6f919 100644 --- a/shared/src/tools/courtIssuedEventCodes.json +++ b/shared/src/tools/courtIssuedEventCodes.json @@ -67,13 +67,13 @@ }, { "eventCode": "OD", - "documentType": "OD - Order of Dismissal Entered,", + "documentType": "OD - Order of Dismissal Entered", "documentTitle": "Order of Dismissal Entered, [Judge Name] [Anything]", "scenario": "Type B" }, { "eventCode": "ODD", - "documentType": "ODD - Order of Dismissal and Decision Entered,", + "documentType": "ODD - Order of Dismissal and Decision Entered", "documentTitle": "Order of Dismissal and Decision Entered, [Judge Name] [Anything]", "scenario": "Type B" }, diff --git a/shared/src/tools/generateMarkdownSchema.js b/shared/src/tools/generateMarkdownSchema.js index 1d2b1c53bc8..cbfb0ba69ff 100644 --- a/shared/src/tools/generateMarkdownSchema.js +++ b/shared/src/tools/generateMarkdownSchema.js @@ -1,5 +1,5 @@ const fs = require('fs'); -const json2md = require('json2md'); +const json2yaml = require('json2yaml'); const { getNextFriendForIncompetentPersonContact, } = require('../business/entities/contacts/NextFriendForIncompetentPersonContact'); @@ -48,177 +48,47 @@ const { const { getSurvivingSpouseContact, } = require('../business/entities/contacts/SurvivingSpouseContact'); +const { + InitialWorkItemMessage, +} = require('../business/entities/InitialWorkItemMessage'); +const { + OrderWithoutBody, +} = require('../business/entities/orders/OrderWithoutBody'); +const { + PrivatePractitioner, +} = require('../business/entities/PrivatePractitioner'); +const { Batch } = require('../business/entities/Batch'); const { Case } = require('../business/entities/cases/Case'); const { CaseDeadline } = require('../business/entities/CaseDeadline'); +const { CaseMessage } = require('../business/entities/CaseMessage'); +const { Correspondence } = require('../business/entities/Correspondence'); const { DocketRecord } = require('../business/entities/DocketRecord'); const { Document } = require('../business/entities/Document'); +const { ForwardMessage } = require('../business/entities/ForwardMessage'); +const { IrsPractitioner } = require('../business/entities/IrsPractitioner'); +const { Message } = require('../business/entities/Message'); +const { NewCaseMessage } = require('../business/entities/NewCaseMessage'); +const { NewPractitioner } = require('../business/entities/NewPractitioner'); +const { Note } = require('../business/entities/notes/Note'); +const { Order } = require('../business/entities/orders/Order'); +const { Practitioner } = require('../business/entities/Practitioner'); +const { PublicUser } = require('../business/entities/PublicUser'); +const { Scan } = require('../business/entities/Scan'); const { Statistic } = require('../business/entities/Statistic'); - -const generateJsonFromSchema = (schema, entityName) => { - let described = schema.describe ? schema.describe().keys : schema; - const fields = Object.keys(described); - - const jsonResult = [{ h1: entityName }]; - - const handleField = (field, fieldName, isAlternative = false, index = 0) => { - const { allow, flags, matches, metas, rules, type } = field; - let presence, description; - - if (flags) { - ({ description, presence } = flags); - } - - const result = []; - - if (!isAlternative) { - if (fieldName) { - result.push({ h3: fieldName }); - } - } else { - result.push({ h4: `Condition #${index + 1} for \`${fieldName}\`: ` }); - } - - if (description) { - result.push({ p: description }); - } - - if (metas) { - metas.forEach(meta => { - if (meta['tags']) { - meta['tags'].forEach(tag => { - result.push({ p: tag }); - }); - } - }); - } - - result.push({ - blockquote: `\`${type === 'alternatives' ? 'conditional' : type}\`${ - presence ? ` | ${presence}` : '' - }`, - }); - - switch (type) { - case 'date': - case 'boolean': - case 'number': - case 'object': - // eslint-disable-next-line spellcheck/spell-checker - // eslint-disable-next-line no-fallthrough - case 'string': - if (rules && rules.length) { - rules.forEach(({ args, name }) => { - let key, value; - switch (name) { - case 'pattern': - result.push({ h5: 'Regex Pattern' }); - result.push({ p: `\`${args.regex}\`` }); - break; - case 'min': - [key, value] = Object.entries(args)[0]; - result.push({ h5: `Minimum ${key}` }); - result.push({ p: `\`${value}\`` }); - break; - case 'max': - [key, value] = Object.entries(args)[0]; - result.push({ h5: `Maximum ${key}` }); - result.push({ p: `\`${value}\`` }); - break; - } - }); - } - - if (allow && allow.length > 1) { - const allowedValues = allow.map( - allowedValue => `\`${allowedValue}\``, - ); - result.push({ h5: 'Allowed Values' }); - result.push({ ul: allowedValues }); - } else if (allow && allow.length === 1) { - result.push({ h5: `Can be ${allow[0]}.` }); - } - break; - - case 'alternatives': - result.push({ p: '*Must match 1 of the following conditions:*' }); - matches.forEach(({ schema: matchSchema }, index) => { - result.push(...handleField(matchSchema, fieldName, true, index)); - }); - break; - - case 'array': - // eslint-disable-next-line no-case-declarations - const { items } = field; - - // array item types (e.g. `.items(joi.object())`) - if (items) { - items.forEach(({ metas, type }) => { - if (metas) { - const metaEntityName = metas[0].entityName; - result.push({ - p: `An array of [\`${metaEntityName}\`](./${metaEntityName}.md)s`, - }); - } else { - result.push({ - p: `An array of ${type}s.`, - }); - } - }); - } - - // array rules (min, max, etc.) - if (rules) { - result.push({ h4: 'Rules' }); - - rules.forEach(({ args, name }) => { - switch (name) { - case 'min': - result.push({ - p: `At least \`${args.limit}\` item(s) must be selected.`, - }); - } - }); - } - break; - - case 'any': - // eslint-disable-next-line no-case-declarations - const { whens } = field; - whens.forEach(({ is, otherwise, ref, then }) => { - result.push({ - p: `If \`${ref.path[0]}\` = \`${is.allow[1]}\`, then this field is \`${then.type}\` and is \`${then.flags.presence}.\` `, - }); - let otherwiseVerbiage = `Otherwise, this field is \`${otherwise.type}\` and is \`${otherwise.flags.presence}\`.`; - if (otherwise.allow && otherwise.allow[0] === null) { - otherwiseVerbiage += ' `null` is allowed.'; - } - result.push({ - p: otherwiseVerbiage, - }); - }); - break; - default: - console.warn(`generateMarkdown: Unrecognized type "${type}"`); - } - - return result; - }; - - fields.forEach(field => { - jsonResult.push(...handleField(described[field], field)); - }); - - return jsonResult; -}; +const { User } = require('../business/entities/User'); +const { UserCase } = require('../business/entities/UserCase'); +const { UserCaseNote } = require('../business/entities/notes/UserCaseNote'); +const { WorkItem } = require('../business/entities/WorkItem'); const generateMarkdownSchema = (entity, entityName) => { - const json = generateJsonFromSchema(entity.getSchema(), entityName); + const json = entity.getSchema().describe(); - fs.writeFileSync(`./docs/entities/${entityName}.md`, json2md(json)); + fs.writeFileSync( + `./docs/entities/${entityName}.md`, + '# ' + entityName + '\n ```\n' + json2yaml.stringify(json) + '\n ```\n', + ); }; -module.exports = { generateJsonFromSchema, generateMarkdownSchema }; - generateMarkdownSchema( getNextFriendForIncompetentPersonContact({ countryType: 'domestic', @@ -347,8 +217,28 @@ generateMarkdownSchema( 'contacts/SurvivingSpouseContact', ); +generateMarkdownSchema(Batch, 'Batch'); generateMarkdownSchema(Case, 'Case'); generateMarkdownSchema(CaseDeadline, 'CaseDeadline'); +generateMarkdownSchema(CaseMessage, 'CaseMessage'); +generateMarkdownSchema(Correspondence, 'Correspondence'); generateMarkdownSchema(DocketRecord, 'DocketRecord'); generateMarkdownSchema(Document, 'Document'); +generateMarkdownSchema(ForwardMessage, 'ForwardMessage'); +generateMarkdownSchema(InitialWorkItemMessage, 'InitialWorkItemMessage'); +generateMarkdownSchema(IrsPractitioner, 'IrsPractitioner'); +generateMarkdownSchema(Message, 'Message'); +generateMarkdownSchema(NewCaseMessage, 'NewCaseMessage'); +generateMarkdownSchema(NewPractitioner, 'NewPractitioner'); +generateMarkdownSchema(Note, 'Note'); +generateMarkdownSchema(Order, 'Order'); +generateMarkdownSchema(OrderWithoutBody, 'OrderWithoutBody'); +generateMarkdownSchema(Practitioner, 'Practitioner'); +generateMarkdownSchema(PrivatePractitioner, 'PrivatePractitioner'); +generateMarkdownSchema(PublicUser, 'PublicUser'); +generateMarkdownSchema(Scan, 'Scan'); generateMarkdownSchema(Statistic, 'Statistic'); +generateMarkdownSchema(User, 'User'); +generateMarkdownSchema(UserCase, 'UserCase'); +generateMarkdownSchema(UserCaseNote, 'UserCaseNote'); +generateMarkdownSchema(WorkItem, 'WorkItem'); diff --git a/shared/src/tools/pdfStyles.js b/shared/src/tools/pdfStyles.js deleted file mode 100644 index 194d5f9f192..00000000000 --- a/shared/src/tools/pdfStyles.js +++ /dev/null @@ -1,83 +0,0 @@ -/* eslint-disable spellcheck/spell-checker */ -exports.pdfStyles = function () { - return ` -@font-face { - font-family: 'nimbus_roman'; - font-style: normal; - font-weight: bold; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -} - -@font-face { - font-family: 'nimbus_roman'; - font-style: italic; - font-weight: bold; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -} - -@font-face { - font-family: 'nimbus_roman'; - font-style: normal; - font-weight: normal; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -} - -@font-face { - font-family: 'nimbus_roman'; - font-style: italic; - font-weight: normal; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -} - -@font-face { - font-family: 'nimbus_sans_l'; - font-style: normal; - font-weight: bold; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -} - -@font-face { - font-family: 'nimbus_sans_l'; - font-style: italic; - font-weight: bold; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -} - -@font-face { - font-family: 'nimbus_sans_l'; - font-style: normal; - font-weight: normal; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -} - -@font-face { - font-family: 'nimbus_sans_l'; - font-style: italic; - font-weight: normal; - src: url(data:application/font-woff2;charset=utf-8;base64,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) - format('woff2'), - url(data:application/font-woff;charset=utf-8;base64,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) - format('woff'); -}`; -}; diff --git a/shared/src/tools/validateUser.js b/shared/src/tools/validateUser.js new file mode 100644 index 00000000000..cfaf84d1961 --- /dev/null +++ b/shared/src/tools/validateUser.js @@ -0,0 +1,30 @@ +const { getUniqueId } = require('../sharedAppContext'); +const { User } = require('../business/entities/User'); + +const [ + email, + role, + section, + name, + judgeFullName, + judgeTitle, +] = process.argv.slice(2); + +const user = new User({ + email, + judgeFullName, + judgeTitle, + name, + role, + section, + userId: getUniqueId(), +}); + +if (!user.isValid()) { + console.error( + 'Validation errors: ', + user.getValidationErrors(), + JSON.stringify(user.toRawObject(), null, 2), + ); + process.exit(1); +} diff --git a/shared/src/utilities/JoiValidationDecorator.js b/shared/src/utilities/JoiValidationDecorator.js index 72e567494a3..4af129ffb1b 100644 --- a/shared/src/utilities/JoiValidationDecorator.js +++ b/shared/src/utilities/JoiValidationDecorator.js @@ -1,4 +1,5 @@ const joi = require('@hapi/joi'); +const { InvalidEntityError } = require('../errors/errors'); const { isEmpty } = require('lodash'); /** @@ -67,7 +68,7 @@ function getFormattedValidationErrors(entity) { const keys = Object.keys(entity); const obj = {}; let errors = null; - if (entity && entity.getFormattedValidationErrors) { + if (entity.getFormattedValidationErrors) { errors = getFormattedValidationErrorsHelper(entity); } if (errors) { @@ -133,12 +134,14 @@ exports.joiValidationDecorator = function ( entityConstructor.prototype.validate = function validate() { if (!this.isValid()) { - throw new Error( - `The ${ - entityConstructor.validationName || '' - } entity was invalid ${JSON.stringify( - this.getFormattedValidationErrors(), - )}`, + const ids = Object.entries(this) + .filter(([key, value]) => value && key.endsWith('Id')) + .map(([key, value]) => `${key}: "${value}"`) + .join('; '); + throw new InvalidEntityError( + JSON.stringify(this.getFormattedValidationErrors()), + entityConstructor.validationName, + ids, ); } return this; diff --git a/shared/src/utilities/generatePDFFromJPGs.js b/shared/src/utilities/generatePDFFromJPGs.js index 392d28f7fc2..a517b45eec9 100644 --- a/shared/src/utilities/generatePDFFromJPGs.js +++ b/shared/src/utilities/generatePDFFromJPGs.js @@ -1,5 +1,3 @@ -const { PDFDocument } = require('pdf-lib'); - /** * takes an array of JPG images (each a byte array) and combines * them into one PDF file @@ -8,7 +6,9 @@ const { PDFDocument } = require('pdf-lib'); * @returns {Uint8Array} byte array of PDF */ -exports.generatePDFFromJPGs = async imgData => { +exports.generatePDFFromJPGs = async (imgData, applicationContext) => { + const { PDFDocument } = await applicationContext.getPdfLib(); + const pdfDoc = await PDFDocument.create(); const addImageToPage = async img => { const imgRef = await pdfDoc.embedJpg(img); diff --git a/shared/src/utilities/generatePDFFromJPGs.test.js b/shared/src/utilities/generatePDFFromJPGs.test.js index 31be3f2195a..e694af4b588 100644 --- a/shared/src/utilities/generatePDFFromJPGs.test.js +++ b/shared/src/utilities/generatePDFFromJPGs.test.js @@ -1,8 +1,10 @@ const fs = require('fs'); const path = require('path'); +const { + applicationContext, +} = require('../business/test/createTestApplicationContext'); const { generatePDFFromJPGs } = require('./generatePDFFromJPGs'); const { PDFDocument } = require('pdf-lib'); - const testAssetsPath = path.join(__dirname, '../../test-assets/'); const testOutputPath = path.join(__dirname, '../../test-output/'); @@ -17,7 +19,10 @@ describe('generatePDFFromJPGs', () => { }); it('creates a pdf document from the specified img data', async () => { - const newPdfData = await generatePDFFromJPGs([testImg, testImg]); + const newPdfData = await generatePDFFromJPGs( + [testImg, testImg], + applicationContext, + ); fs.writeFileSync(testOutputPath + 'generatePDFFromJPGData.pdf', newPdfData); diff --git a/shared/src/utilities/getAllEventCodes.js b/shared/src/utilities/getAllEventCodes.js index f47fa701080..3f203a21e67 100644 --- a/shared/src/utilities/getAllEventCodes.js +++ b/shared/src/utilities/getAllEventCodes.js @@ -1,7 +1,7 @@ const courtIssuedEventCodes = require('../tools/courtIssuedEventCodes.json'); const documentMapExternal = require('../tools/externalFilingEvents.json'); const documentMapInternal = require('../tools/internalFilingEvents.json'); -const { Document } = require('../business/entities/Document'); +const { EVENT_CODES } = require('../business/entities/EntityConstants'); const eventCodes = []; for (const category in documentMapExternal) { @@ -18,9 +18,7 @@ for (const document of courtIssuedEventCodes) { eventCodes.push(document.eventCode); } -const results = Array.from( - new Set(eventCodes.concat(Document.eventCodes)), -).sort(); +const results = Array.from(new Set(eventCodes.concat(EVENT_CODES))).sort(); exports.getAllEventCodes = () => { return results; diff --git a/validateServerless.js b/validateServerless.js new file mode 100644 index 00000000000..662d3d6b935 --- /dev/null +++ b/validateServerless.js @@ -0,0 +1,62 @@ +const fs = require('fs'); +const glob = require('glob'); + +let errorsFound = 0; + +glob('web-api/serverless-*.yml', pathMethodLint); + +/** + * + */ +function pathMethodLint(err, files) { + if (err) { + console.error(err); + return; + } + for (const file of files) { + let endpointCollection = {}; + let endpoint = {}; + const lines = fs.readFileSync(file).toString().split('\n'); + + lines.forEach(line => { + let matches; + if ((matches = line.match(/(path|method|handler):\s(\S+)/))) { + endpoint[matches[1]] = matches[2]; + } + if (endpoint.method && endpoint.path) { + const simplified = `${endpoint.method} ${endpoint.path.replace( + /\{\w+\}/, + '{var}', + )}`; + endpointCollection[simplified] = endpointCollection[simplified] || []; + endpointCollection[simplified].push(endpoint); + endpoint = {}; + } + }); + + Object.entries(endpointCollection).forEach(endpointIsUnique); + } + + if (errorsFound > 0) { + console.error( + `FAIL: Serverless configuration linting - ${errorsFound} errors`, + ); + process.exit(1); + } else { + console.info('PASS: Serverless configuration linting'); + process.exit(); + } +} + +/** + * + */ +function endpointIsUnique([simplifiedPath, endpointsWithin]) { + if (endpointsWithin.length > 1) { + errorsFound++; + console.error( + `Likely endpoint conflict on ${simplifiedPath}:\n`, + JSON.stringify(endpointsWithin, null, 2), + ); + } +} diff --git a/verify-sonarqube-passed.sh b/verify-sonarqube-passed.sh index 95c215dcc37..97af60dca3e 100755 --- a/verify-sonarqube-passed.sh +++ b/verify-sonarqube-passed.sh @@ -28,4 +28,4 @@ done if [[ $STATUS != 'SUCCESS' ]] ; then echo "SonarQube Failed" exit 1; -fi \ No newline at end of file +fi diff --git a/web-api/config/custom.yml b/web-api/config/custom.yml new file mode 100644 index 00000000000..56562260fc5 --- /dev/null +++ b/web-api/config/custom.yml @@ -0,0 +1,244 @@ +customDefault: &customDefault + logForwarding: + destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} + filterPattern: 'ERROR' + + jetpack: + mode: npm + lockfile: ../package-lock.json + + prune: + automatic: true + number: 3 + + alerts: + stages: + - dev + - stg + - test + - prod + dashboards: true + alarms: + - errorExceptions + definitions: + errorExceptions: + description: 'Internal Error Exception Alarm' + namespace: 'AWS/Lambda' + metric: Errors + threshold: 1 + statistic: Sum + period: 60 + evaluationPeriods: 1 + comparisonOperator: GreaterThanThreshold + pattern: 'ERROR' + topics: + alarm: + topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} + + splitStacks: + perFunction: true + perType: true + + stage: ${opt:stage, 'dev'} + + region: ${opt:region, 'us-east-1'} + + vars: ${file(./config/${opt:stage, 'dev'}.yml)} + +serverless-offline: &serverless-offline-default + skipCacheInvalidation: ${opt:skipCacheInvalidation} + host: 0.0.0.0 + +customDomainDefault: &customDomainDefault + domainName: efcms-${self:provider.stage}.${opt:domain} + endpointType: 'regional' + certificateName: efcms-${self:provider.stage}.${opt:domain} + certificateRegion: ${opt:region} + stage: ${self:provider.stage} + createRoute53Record: false + enabled: true + +api: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3001 + customDomain: + <<: *customDomainDefault + basePath: 'api' + +cases: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3002 + customDomain: + <<: *customDomainDefault + basePath: 'cases' + +users: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3003 + customDomain: + <<: *customDomainDefault + basePath: 'users' + +documents: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3004 + customDomain: + <<: *customDomainDefault + basePath: 'documents' + +work-items: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3005 + customDomain: + <<: *customDomainDefault + basePath: 'work-items' + +sections: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3006 + customDomain: + <<: *customDomainDefault + basePath: 'sections' + +trial-sessions: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3007 + customDomain: + <<: *customDomainDefault + basePath: 'trial-sessions' + +case-documents: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3008 + customDomain: + <<: *customDomainDefault + basePath: 'case-documents' + +case-deadlines: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3009 + customDomain: + <<: *customDomainDefault + basePath: 'case-deadlines' + +case-notes: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3010 + customDomain: + <<: *customDomainDefault + basePath: 'case-notes' + +notifications: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3020 # this port really isn't used, but needs to be unique to prevent address in use errors + websocketPort: 3011 + customDomain: + websocket: + domainName: efcms-${self:provider.stage}-ws.${opt:domain} + endpointType: 'regional' + certificateName: efcms-${self:provider.stage}-ws.${opt:domain} + certificateRegion: ${opt:region} + stage: ${self:provider.stage} + createRoute53Record: false + enabled: true + +streams: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3012 + serverless-offline-dynamodb-streams: + apiVersion: '2013-12-02' + endpoint: http://0.0.0.0:8000 + region: us-east-1 + accessKeyId: root + secretAccessKey: root + readInterval: 500 + customDomain: + <<: *customDomainDefault + basePath: 'streams' + +public-api: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3013 + customDomain: + <<: *customDomainDefault + basePath: 'public-api' + +case-parties: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3014 + customDomain: + <<: *customDomainDefault + basePath: 'case-parties' + +case-meta: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3015 + customDomain: + <<: *customDomainDefault + basePath: 'case-meta' + +reports: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3016 + customDomain: + <<: *customDomainDefault + basePath: 'reports' + +practitioners: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3017 + customDomain: + <<: *customDomainDefault + basePath: 'practitioners' + +messages: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3018 + customDomain: + <<: *customDomainDefault + basePath: 'messages' + +migrate: + <<: *customDefault + serverless-offline: + <<: *serverless-offline-default + port: 3030 + customDomain: + <<: *customDomainDefault + basePath: 'migrate' diff --git a/web-api/config/environment-variables.yml b/web-api/config/environment-variables.yml new file mode 100644 index 00000000000..f1d72ac40ea --- /dev/null +++ b/web-api/config/environment-variables.yml @@ -0,0 +1,18 @@ +S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} +DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 +TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 +DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} +MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} +ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} +MASTER_REGION: ${self:provider.masterRegion} +STAGE: ${self:custom.stage} +USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} +USER_POOL_IRS_ID: ${opt:userPoolIrsId, self.provider.userPoolIrsId} +NODE_ENV: ${self:custom.vars.nodeEnv} +EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} +EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} +EMAIL_SERVED_PETITION_TEMPLATE: petition_served_${opt:stage} +EFCMS_DOMAIN: ${opt:domain} +CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} +CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} +IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} diff --git a/web-api/config/exp.yml b/web-api/config/exp1.yml similarity index 100% rename from web-api/config/exp.yml rename to web-api/config/exp1.yml diff --git a/web-api/config/resources.yml b/web-api/config/resources.yml new file mode 100644 index 00000000000..95534ed48bb --- /dev/null +++ b/web-api/config/resources.yml @@ -0,0 +1,14 @@ +Resources: + ##### + # This Authorizer is used for validating the JWT token before invoking the lambda + ##### + ApiGatewayAuthorizer: + Type: AWS::ApiGateway::Authorizer + Properties: + Name: CognitoUserPool + Type: COGNITO_USER_POOLS + IdentitySource: method.request.header.Authorization + RestApiId: + Ref: ApiGatewayRestApi + ProviderARNs: + - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} diff --git a/web-api/elasticsearch/elasticsearch-indexes.js b/web-api/elasticsearch/elasticsearch-indexes.js index fc089f05dd5..b2e92767bc2 100644 --- a/web-api/elasticsearch/elasticsearch-indexes.js +++ b/web-api/elasticsearch/elasticsearch-indexes.js @@ -1 +1,7 @@ -exports.elasticsearchIndexes = ['efcms-case', 'efcms-document', 'efcms-user']; +exports.elasticsearchIndexes = [ + 'efcms-case', + 'efcms-document', + 'efcms-user', + 'efcms-message', + 'efcms-user-case', +]; diff --git a/web-api/migrations/00001-sample-migration.js b/web-api/migrations/00001-sample-migration.js deleted file mode 100644 index 86138b28db4..00000000000 --- a/web-api/migrations/00001-sample-migration.js +++ /dev/null @@ -1,7 +0,0 @@ -const { upGenerator } = require('./utilities'); - -const mutateRecord = () => { - //migration goes here -}; - -module.exports = { mutateRecord, up: upGenerator(mutateRecord) }; diff --git a/web-api/migrations/00001-statistics.js b/web-api/migrations/00001-statistics.js new file mode 100644 index 00000000000..fbabf8014c5 --- /dev/null +++ b/web-api/migrations/00001-statistics.js @@ -0,0 +1,53 @@ +const createApplicationContext = require('../src/applicationContext'); +const { + CASE_TYPES_MAP, +} = require('../../shared/src/business/entities/EntityConstants'); +const { Case } = require('../../shared/src/business/entities/cases/Case'); +const { isCaseRecord, upGenerator } = require('./utilities'); +const { Statistic } = require('../../shared/src/business/entities/Statistic'); +const applicationContext = createApplicationContext({}); + +const mutateRecord = item => { + if ( + isCaseRecord(item) && + item.caseType === CASE_TYPES_MAP.deficiency && + item.hasVerifiedIrsNotice === true + ) { + let { statistics } = item; + if (!statistics || statistics.length === 0) { + const defaultStatistic = new Statistic( + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: '2012', + yearOrPeriod: 'Year', + }, + { applicationContext }, + ); + statistics = [defaultStatistic]; + } else { + statistics.map(statistic => { + if (statistic.deficiencyAmount) { + statistic.irsDeficiencyAmount = statistic.deficiencyAmount; + } + if (statistic.totalPenalties) { + statistic.irsTotalPenalties = statistic.totalPenalties; + } + }); + } + + const caseEntity = new Case( + { + ...item, + statistics, + }, + { applicationContext }, + ) + .validate() + .toRawObject(); + + return { ...item, ...caseEntity }; + } +}; + +module.exports = { mutateRecord, up: upGenerator(mutateRecord) }; diff --git a/web-api/migrations/00001-statistics.test.js b/web-api/migrations/00001-statistics.test.js new file mode 100644 index 00000000000..22580394ff0 --- /dev/null +++ b/web-api/migrations/00001-statistics.test.js @@ -0,0 +1,216 @@ +const { + CASE_TYPES_MAP, +} = require('../../shared/src/business/entities/EntityConstants'); +const { forAllRecords } = require('./utilities'); +const { MOCK_CASE } = require('../../shared/src/test/mockCase'); +const { up } = require('./00001-statistics'); + +describe('case statistics migration', () => { + let applicationContext; + let scanStub; + let putStub; + + const mockCaseWithKeys = { + ...MOCK_CASE, + pk: `case|${MOCK_CASE.caseId}`, + sk: `case|${MOCK_CASE.caseId}`, + }; + + beforeEach(() => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockCaseWithKeys], + }), + }); + + putStub = jest.fn().mockReturnValue({ + promise: async () => ({}), + }); + + applicationContext = { + environment: { + stage: 'dev', + }, + getDocumentClient: () => ({ + put: putStub, + scan: scanStub, + }), + }; + }); + + it('should not update the item when it is not a case', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockCaseWithKeys], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(0); + }); + + it('should not update the item when its case type is not deficiency', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.cdp, + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(0); + }); + + it('should not update the item when hasVerifiedIrsNotice is false', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: false, + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(0); + }); + + it('should update the item with a default statistic if it does not have one', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: true, + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + expect(putStub.mock.calls[0][0].Item).toMatchObject({ + statistics: [ + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: '2012', + yearOrPeriod: 'Year', + }, + ], + }); + }); + + it('should update the item with a default statistic if its statistics array is length 0', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: true, + statistics: [], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + expect(putStub.mock.calls[0][0].Item).toMatchObject({ + statistics: [ + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: '2012', + yearOrPeriod: 'Year', + }, + ], + }); + }); + + it('should update the item with new statistic values if statistics are already present on the case', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: true, + statistics: [ + { + deficiencyAmount: 123, + totalPenalties: 123, + year: '2015', + yearOrPeriod: 'Year', + }, + ], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + expect(putStub.mock.calls[0][0].Item).toMatchObject({ + statistics: [ + { + irsDeficiencyAmount: 123, + irsTotalPenalties: 123, + year: '2015', + yearOrPeriod: 'Year', + }, + ], + }); + }); + + it('should not change the item if its statistic values are already updated', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: true, + statistics: [ + { + irsDeficiencyAmount: 123, + irsTotalPenalties: 123, + year: '2015', + yearOrPeriod: 'Year', + }, + ], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + expect(putStub.mock.calls[0][0].Item).toMatchObject({ + statistics: [ + { + irsDeficiencyAmount: 123, + irsTotalPenalties: 123, + year: '2015', + yearOrPeriod: 'Year', + }, + ], + }); + }); +}); diff --git a/web-api/migrations/00002-invalid-statistics.js b/web-api/migrations/00002-invalid-statistics.js new file mode 100644 index 00000000000..8bd6d1165fb --- /dev/null +++ b/web-api/migrations/00002-invalid-statistics.js @@ -0,0 +1,33 @@ +const createApplicationContext = require('../src/applicationContext'); +const { + CASE_TYPES_MAP, +} = require('../../shared/src/business/entities/EntityConstants'); +const { Case } = require('../../shared/src/business/entities/cases/Case'); +const { isCaseRecord, upGenerator } = require('./utilities'); +const applicationContext = createApplicationContext({}); + +const mutateRecord = item => { + let { statistics } = item; + + if ( + isCaseRecord(item) && + (item.caseType !== CASE_TYPES_MAP.deficiency || + item.hasVerifiedIrsNotice !== true) && + statistics && + statistics.length + ) { + const caseEntity = new Case( + { + ...item, + statistics: [], + }, + { applicationContext }, + ) + .validate() + .toRawObject(); + + return { ...item, ...caseEntity }; + } +}; + +module.exports = { mutateRecord, up: upGenerator(mutateRecord) }; diff --git a/web-api/migrations/00002-invalid-statistics.test.js b/web-api/migrations/00002-invalid-statistics.test.js new file mode 100644 index 00000000000..d38aeae35a2 --- /dev/null +++ b/web-api/migrations/00002-invalid-statistics.test.js @@ -0,0 +1,158 @@ +const { + CASE_TYPES_MAP, +} = require('../../shared/src/business/entities/EntityConstants'); +const { forAllRecords } = require('./utilities'); +const { MOCK_CASE } = require('../../shared/src/test/mockCase'); +const { up } = require('./00002-invalid-statistics'); + +describe('invalid statistics migration', () => { + let applicationContext; + let scanStub; + let putStub; + + const mockCaseWithKeys = { + ...MOCK_CASE, + pk: `case|${MOCK_CASE.caseId}`, + sk: `case|${MOCK_CASE.caseId}`, + }; + + beforeEach(() => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockCaseWithKeys], + }), + }); + + putStub = jest.fn().mockReturnValue({ + promise: async () => ({}), + }); + + applicationContext = { + environment: { + stage: 'dev', + }, + getDocumentClient: () => ({ + put: putStub, + scan: scanStub, + }), + }; + }); + + it('should wipe out any existing statistics if hasVerifiedIrsNotice is false', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: false, + statistics: [ + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: '2012', + yearOrPeriod: 'Year', + }, + ], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + expect(putStub.mock.calls[0][0].Item).toMatchObject({ + statistics: [], + }); + }); + + it('should wipe out any existing statistics if case is not a deficiency', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.cdp, + hasVerifiedIrsNotice: true, + statistics: [ + { + irsDeficiencyAmount: 1, + irsTotalPenalties: 1, + year: '2012', + yearOrPeriod: 'Year', + }, + ], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + expect(putStub.mock.calls[0][0].Item).toMatchObject({ + statistics: [], + }); + }); + + it('should not wipe out any existing statistics if case is not a deficiency and has no statistics', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.cdp, + hasVerifiedIrsNotice: true, + statistics: [], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(0); + }); + + it('should wipe out any existing statistics if case is not a deficiency, has no verified irs notice, and has no statistics', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.cdp, + hasVerifiedIrsNotice: false, + statistics: [{ yearOrPeriod: 'Year' }], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + expect(putStub.mock.calls[0][0].Item).toMatchObject({ + statistics: [], + }); + }); + + it('should not wipe out any existing statistics if deficiency case hasVerifiedIrsNotice is false and has no statistics', async () => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockCaseWithKeys, + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: false, + statistics: [], + }, + ], + }), + }); + + await up(applicationContext.getDocumentClient(), '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(0); + }); +}); diff --git a/web-api/migrations/00003-user-case-mapping.js b/web-api/migrations/00003-user-case-mapping.js new file mode 100644 index 00000000000..bde3f73cbb8 --- /dev/null +++ b/web-api/migrations/00003-user-case-mapping.js @@ -0,0 +1,43 @@ +const createApplicationContext = require('../src/applicationContext'); +const { + isNewUserCaseMappingRecord, + isUserCaseMappingRecord, + upGenerator, +} = require('./utilities'); +const { Case } = require('../../shared/src/business/entities/cases/Case'); +const { UserCase } = require('../../shared/src/business/entities/UserCase'); +const applicationContext = createApplicationContext({}); + +const mutateRecord = async (item, documentClient, tableName) => { + const caseId = item.sk.split('|')[1]; + + if (isUserCaseMappingRecord(item) && !isNewUserCaseMappingRecord(item)) { + const mappedCase = await documentClient + .get({ + Key: { + pk: `case|${caseId}`, + sk: `case|${caseId}`, + }, + TableName: tableName, + }) + .promise(); + + if (mappedCase.Item) { + const caseEntity = new Case(mappedCase.Item, { applicationContext }); + const userCaseEntity = new UserCase(caseEntity.validate().toRawObject(), { + applicationContext, + }) + .validate() + .toRawObject(); + + return { + ...item, + ...userCaseEntity, + entityName: 'UserCase', + gsi1pk: `user-case|${caseId}`, + }; + } + } +}; + +module.exports = { mutateRecord, up: upGenerator(mutateRecord) }; diff --git a/web-api/migrations/00003-user-case-mapping.test.js b/web-api/migrations/00003-user-case-mapping.test.js new file mode 100644 index 00000000000..8ba137565df --- /dev/null +++ b/web-api/migrations/00003-user-case-mapping.test.js @@ -0,0 +1,85 @@ +const { forAllRecords } = require('./utilities'); +const { MOCK_CASE } = require('../../shared/src/test/mockCase'); +const { up } = require('./00003-user-case-mapping'); +const { UserCase } = require('../../shared/src/business/entities/UserCase'); + +describe('user case mapping migration', () => { + let documentClient; + let scanStub; + let putStub; + let getStub; + + const mockUserCaseItem = { + pk: 'user|6805d1ab-18d0-43ec-bafb-654e83405416', + sk: `case|${MOCK_CASE.caseId}`, + }; + + const mockUserCaseEntity = new UserCase(MOCK_CASE).validate().toRawObject(); + + const mockNewUserCaseItem = { + gsi1pk: `user-case|${MOCK_CASE.caseId}`, + pk: 'user|6805d1ab-18d0-43ec-bafb-654e83405416', + sk: `case|${MOCK_CASE.caseId}`, + ...mockUserCaseEntity, + }; + + const mockCaseWithKeys = { + ...MOCK_CASE, + pk: `case|${MOCK_CASE.caseId}`, + sk: `case|${MOCK_CASE.caseId}`, + }; + + beforeEach(() => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockUserCaseItem], + }), + }); + + putStub = jest.fn().mockReturnValue({ + promise: async () => ({}), + }); + + getStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Item: mockCaseWithKeys, + }), + }); + + documentClient = { + get: getStub, + put: putStub, + scan: scanStub, + }; + }); + + it('should not update the item when it is not a user-case record', async () => { + documentClient.scan = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockCaseWithKeys], + }), + }); + + await up(documentClient, '', forAllRecords); + + expect(putStub).not.toHaveBeenCalled(); + }); + + it('should not update the item when its a new user-case record', async () => { + documentClient.scan = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockNewUserCaseItem], + }), + }); + + await up(documentClient, '', forAllRecords); + + expect(putStub).not.toHaveBeenCalled(); + }); + + it('should update the item', async () => { + await up(documentClient, '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + }); +}); diff --git a/web-api/migrations/00004-user-case-status.js b/web-api/migrations/00004-user-case-status.js new file mode 100644 index 00000000000..896130d7c5d --- /dev/null +++ b/web-api/migrations/00004-user-case-status.js @@ -0,0 +1,36 @@ +const createApplicationContext = require('../src/applicationContext'); +const { Case } = require('../../shared/src/business/entities/cases/Case'); +const { isUserCaseMappingRecord, upGenerator } = require('./utilities'); +const { UserCase } = require('../../shared/src/business/entities/UserCase'); +const applicationContext = createApplicationContext({}); + +const mutateRecord = async (item, documentClient, tableName) => { + if (isUserCaseMappingRecord(item) && !item.status) { + const caseId = item.sk.split('|')[1]; + const mappedCase = await documentClient + .get({ + Key: { + pk: `case|${caseId}`, + sk: `case|${caseId}`, + }, + TableName: tableName, + }) + .promise(); + + if (mappedCase.Item) { + const caseEntity = new Case(mappedCase.Item, { applicationContext }); + const userCaseEntity = new UserCase(caseEntity.validate().toRawObject(), { + applicationContext, + }) + .validate() + .toRawObject(); + + return { + ...item, + ...userCaseEntity, + }; + } + } +}; + +module.exports = { mutateRecord, up: upGenerator(mutateRecord) }; diff --git a/web-api/migrations/00004-user-case-status.test.js b/web-api/migrations/00004-user-case-status.test.js new file mode 100644 index 00000000000..62c0151de70 --- /dev/null +++ b/web-api/migrations/00004-user-case-status.test.js @@ -0,0 +1,93 @@ +const { + CASE_STATUS_TYPES, +} = require('../../shared/src/business/entities/EntityConstants'); +const { forAllRecords } = require('./utilities'); +const { MOCK_CASE } = require('../../shared/src/test/mockCase'); +const { up } = require('./00004-user-case-status'); +const { UserCase } = require('../../shared/src/business/entities/UserCase'); + +describe('user case mapping migration', () => { + let documentClient; + let scanStub; + let putStub; + let getStub; + + const mockUserCaseItem = { + pk: 'user|6805d1ab-18d0-43ec-bafb-654e83405416', + sk: `case|${MOCK_CASE.caseId}`, + }; + + const mockUserCaseEntity = new UserCase(MOCK_CASE).validate().toRawObject(); + + const mockNewUserCaseItem = { + gsi1pk: `user-case|${MOCK_CASE.caseId}`, + pk: 'user|6805d1ab-18d0-43ec-bafb-654e83405416', + sk: `case|${MOCK_CASE.caseId}`, + ...mockUserCaseEntity, + }; + + const mockCaseWithKeys = { + ...MOCK_CASE, + pk: `case|${MOCK_CASE.caseId}`, + sk: `case|${MOCK_CASE.caseId}`, + }; + + beforeEach(() => { + scanStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockUserCaseItem], + }), + }); + + putStub = jest.fn().mockReturnValue({ + promise: async () => ({}), + }); + + getStub = jest.fn().mockReturnValue({ + promise: async () => ({ + Item: mockCaseWithKeys, + }), + }); + + documentClient = { + get: getStub, + put: putStub, + scan: scanStub, + }; + }); + + it('should not update the item when it is not a user-case record', async () => { + documentClient.scan = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [mockCaseWithKeys], + }), + }); + + await up(documentClient, '', forAllRecords); + + expect(putStub).not.toHaveBeenCalled(); + }); + + it('should not update the item when it has a status', async () => { + documentClient.scan = jest.fn().mockReturnValue({ + promise: async () => ({ + Items: [ + { + ...mockNewUserCaseItem, + status: CASE_STATUS_TYPES.closed, + }, + ], + }), + }); + + await up(documentClient, '', forAllRecords); + + expect(putStub).not.toHaveBeenCalled(); + }); + + it('should update the item', async () => { + await up(documentClient, '', forAllRecords); + + expect(putStub.mock.calls.length).toBe(1); + }); +}); diff --git a/web-api/migrations/utilities.js b/web-api/migrations/utilities.js index deb8588d25b..f05d05210f0 100644 --- a/web-api/migrations/utilities.js +++ b/web-api/migrations/utilities.js @@ -1,6 +1,10 @@ const isCaseRecord = item => !!item.caseType; const isTrialSessionRecord = item => !!item.caseOrder && !!item.trialSessionId && !!item.maxCases; +const isUserCaseMappingRecord = item => + item.pk.startsWith('user|') && item.sk.startsWith('case|'); +const isNewUserCaseMappingRecord = item => + !!item.gsi1pk && item.gsi1pk.startsWith('user-case|'); const forAllRecords = async (documentClient, tableName, cb) => { let hasMoreResults = true; @@ -48,6 +52,8 @@ const upGenerator = mutateFunction => async ( module.exports = { forAllRecords, isCaseRecord, + isNewUserCaseMappingRecord, isTrialSessionRecord, + isUserCaseMappingRecord, upGenerator, }; diff --git a/web-api/migrations/utilities.test.js b/web-api/migrations/utilities.test.js index f1988aab729..6bac7f70c9e 100644 --- a/web-api/migrations/utilities.test.js +++ b/web-api/migrations/utilities.test.js @@ -1,16 +1,20 @@ +const { + CASE_TYPES_MAP, +} = require('../../shared/src/business/entities/EntityConstants'); const { forAllRecords, isCaseRecord, + isNewUserCaseMappingRecord, isTrialSessionRecord, + isUserCaseMappingRecord, upGenerator, } = require('./utilities'); -const { Case } = require('../../shared/src/business/entities/cases/Case'); describe('utilities', () => { describe('isCaseRecord', () => { it('should return true if the item is a case record', () => { const result = isCaseRecord({ - caseType: Case.CASE_TYPES_MAP.cdp, + caseType: CASE_TYPES_MAP.cdp, }); expect(result).toEqual(true); @@ -38,11 +42,63 @@ describe('utilities', () => { it('should return false if the item is not a trial session record', () => { const result = isTrialSessionRecord({ - caseType: Case.CASE_TYPES_MAP.cdp, + caseType: CASE_TYPES_MAP.cdp, + }); + + expect(result).toEqual(false); + }); + }); + + describe('isUserCaseMappingRecord', () => { + it('should return true if the item is a user case mapping record', () => { + const result = isUserCaseMappingRecord({ + pk: 'user|', + sk: 'case|', + }); + + expect(result).toEqual(true); + }); + + it('should return false if the item is not a user case mapping record (pk,sk = case|)', () => { + const result = isUserCaseMappingRecord({ + pk: 'case|', + sk: 'case|', }); expect(result).toEqual(false); }); + + it('should return false if the item is not a user case mapping record (pk,sk = user|)', () => { + const result = isUserCaseMappingRecord({ + pk: 'user|', + sk: 'user|', + }); + expect(result).toEqual(false); + }); + }); + + describe('isNewUserCaseMappingRecord', () => { + it('should return true if the record is a new user-case mapping record', () => { + const result = isNewUserCaseMappingRecord({ + gsi1pk: 'user-case|', + }); + + expect(result).toEqual(true); + }); + + it('should return false if the record is not a new user-case mapping record', () => { + const result = isNewUserCaseMappingRecord({ + gsi1pk: 'case|', + }); + + expect(result).toEqual(false); + }); + + it('should return false if the record is not a new user-case mapping record (no gsi1pk)', () => { + const result = isNewUserCaseMappingRecord({}); + + expect(result).toEqual(false); + }); }); describe('forAllRecords', () => { @@ -54,11 +110,11 @@ describe('utilities', () => { scannedItems = [ { caseId: 'case-123', - caseType: Case.CASE_TYPES_MAP.cdp, + caseType: CASE_TYPES_MAP.cdp, }, { caseId: 'case-321', - caseType: Case.CASE_TYPES_MAP.deficiency, + caseType: CASE_TYPES_MAP.deficiency, }, ]; @@ -94,11 +150,11 @@ describe('utilities', () => { scannedItems = [ { caseId: 'case-123', - caseType: Case.CASE_TYPES_MAP.cdp, + caseType: CASE_TYPES_MAP.cdp, }, { caseId: 'case-321', - caseType: Case.CASE_TYPES_MAP.deficiency, + caseType: CASE_TYPES_MAP.deficiency, }, ]; diff --git a/web-api/proxy.js b/web-api/proxy.js index 68f18ca6c27..3e32a60293d 100644 --- a/web-api/proxy.js +++ b/web-api/proxy.js @@ -20,6 +20,7 @@ const PROXY_DESTINATIONS = { '/case-parties': `http://${PROXY_HOST}:3014`, '/cases': `http://${PROXY_HOST}:3002`, '/documents': `http://${PROXY_HOST}:3004`, + '/messages': `http://${PROXY_HOST}:3018`, '/migrate': `http://${PROXY_HOST}:3030`, '/notifications': `http://${PROXY_HOST}:3011`, '/practitioners': `http://${PROXY_HOST}:3017`, diff --git a/web-api/run-serverless-messages.sh b/web-api/run-serverless-messages.sh new file mode 100755 index 00000000000..013aac73d54 --- /dev/null +++ b/web-api/run-serverless-messages.sh @@ -0,0 +1,3 @@ +#!/bin/bash -e + +./web-api/run-serverless.sh "${1}" "${2}" "messagesHandlers.js" "serverless-messages.yml" "build:api:messages" diff --git a/web-api/run-serverless.sh b/web-api/run-serverless.sh index f7268abc7c3..628a40c7fbe 100755 --- a/web-api/run-serverless.sh +++ b/web-api/run-serverless.sh @@ -51,6 +51,17 @@ export SLS_DEBUG="*" # temp fix until serverless-domain-manager issue is resolved NEW_COLOR="green" +handlerCkSum=$(cksum "./web-api/src/${handler}" | awk '{print $1}') +configCkSum=$(cksum "./web-api/${config}" | awk '{print $1}') +lockCkSum=$(cksum package-lock.json | awk '{print $1}') +ckSum="${handlerCkSum} ${lockCkSum} ${configCkSum}" +deployedCkSum=$(aws dynamodb get-item --region us-east-1 --table-name "efcms-deploy-${slsStage}" --key '{"pk":{"S":"check-sum-'"${region}"'-'"${config}"'-'"${NEW_COLOR}"'"},"sk":{"S":"check-sum-'"${region}"'-'"${config}"'-'"${NEW_COLOR}"'"}}' | jq -r ".Item.cksum.S") + +if [ "${deployedCkSum}" == "${ckSum}" ] ; then + echo "check sums were equal, skipping the stack deploy" + exit 0; +fi + set -- \ --accountId "${ACCOUNT_ID}" \ --config "./web-api/${config}" \ @@ -80,3 +91,5 @@ echo "slsStage: ${slsStage}" echo "region: ${region}" cp "/tmp/${handler}" src + +aws dynamodb put-item --region us-east-1 --table-name "efcms-deploy-${slsStage}" --item '{"pk":{"S":"check-sum-'"${region}"'-'"${config}"'-'"${NEW_COLOR}"'"},"sk":{"S":"check-sum-'"${region}"'-'"${config}"'-'"${NEW_COLOR}"'"},"cksum":{"S":"'"${ckSum}"'"}}' diff --git a/web-api/seed-elasticsearch.sh b/web-api/seed-elasticsearch.sh index 5ba100d01ac..b7eaa5796f3 100755 --- a/web-api/seed-elasticsearch.sh +++ b/web-api/seed-elasticsearch.sh @@ -3,4 +3,6 @@ curl -X DELETE "localhost:9200/efcms-case" curl -X DELETE "localhost:9200/efcms-document" curl -X DELETE "localhost:9200/efcms-user" +curl -X DELETE "localhost:9200/efcms-message" +curl -X DELETE "localhost:9200/efcms-user-case" ELASTICSEARCH_PORT=9200 ELASTICSEARCH_PROTOCOL="http" node ./web-api/elasticsearch/elasticsearch-index-settings.js diff --git a/web-api/serverless-api.yml b/web-api/serverless-api.yml index e63810e1c33..d061807dc7b 100644 --- a/web-api/serverless-api.yml +++ b/web-api/serverless-api.yml @@ -1,77 +1,15 @@ service: ef-cms-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3001 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'api' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):api} provider: name: aws @@ -99,19 +37,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -120,39 +46,7 @@ package: - web-api/${self:provider.dir}/apiHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: getNotifications: diff --git a/web-api/serverless-case-deadlines.yml b/web-api/serverless-case-deadlines.yml index e153fdfbd9b..6243c88a67a 100644 --- a/web-api/serverless-case-deadlines.yml +++ b/web-api/serverless-case-deadlines.yml @@ -1,75 +1,14 @@ service: ef-cms-case-deadlines-${opt:stageColor} plugins: - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3009 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'case-deadlines' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):case-deadlines} provider: name: aws @@ -96,20 +35,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -118,39 +44,7 @@ package: - web-api/${self:provider.dir}/caseDeadlinesHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: createCaseDeadline: diff --git a/web-api/serverless-case-documents.yml b/web-api/serverless-case-documents.yml index f3b76d69ed8..456240b89b3 100644 --- a/web-api/serverless-case-documents.yml +++ b/web-api/serverless-case-documents.yml @@ -1,75 +1,14 @@ service: ef-cms-case-docs-${opt:stageColor} plugins: - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3008 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'case-documents' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):case-documents} provider: name: aws @@ -97,23 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -123,39 +46,7 @@ package: excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: serveCourtIssuedDocument: @@ -414,13 +305,25 @@ functions: Ref: ApiGatewayAuthorizer updateCorrespondenceDocument: - handler: web-api/${self:provider.dir}/caseDocumentsHandlers.updateCorrespondenceDocument + handler: web-api/${self:provider.dir}/caseDocumentsHandlers.updateCorrespondenceDocumentLambda events: - http: - path: /{caseId}/correspondence/{documentIdToEdit} + path: /{caseId}/correspondence/{documentId} method: put cors: ui-${self:provider.stage}.ef-cms.${opt:domain} authorizer: type: COGNITO_USER_POOLS authorizerId: Ref: ApiGatewayAuthorizer + + deleteCorrespondenceDocument: + handler: web-api/${self:provider.dir}/caseDocumentsHandlers.deleteCorrespondenceDocumentLambda + events: + - http: + path: /{caseId}/correspondence/{documentId} + method: delete + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer diff --git a/web-api/serverless-case-meta.yml b/web-api/serverless-case-meta.yml index 4a291520e1c..f40bb1d3fef 100644 --- a/web-api/serverless-case-meta.yml +++ b/web-api/serverless-case-meta.yml @@ -1,76 +1,14 @@ service: ef-cms-case-meta-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3015 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'case-meta' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):case-meta} provider: name: aws @@ -98,23 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -123,39 +45,7 @@ package: - web-api/${self:provider.dir}/caseMetaHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: updateCaseTrialSortTags: @@ -277,3 +167,51 @@ functions: type: COGNITO_USER_POOLS authorizerId: Ref: ApiGatewayAuthorizer + + updateOtherStatistics: + handler: web-api/${self:provider.dir}/caseMetaHandlers.updateOtherStatisticsLambda + events: + - http: + path: /{caseId}/other-statistics + method: post + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + addDeficiencyStatistic: + handler: web-api/${self:provider.dir}/caseMetaHandlers.addDeficiencyStatisticLambda + events: + - http: + path: /{caseId}/statistics + method: post + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + updateDeficiencyStatistic: + handler: web-api/${self:provider.dir}/caseMetaHandlers.updateDeficiencyStatisticLambda + events: + - http: + path: /{caseId}/statistics/{statisticId} + method: put + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + deleteDeficiencyStatistic: + handler: web-api/${self:provider.dir}/caseMetaHandlers.deleteDeficiencyStatisticLambda + events: + - http: + path: /{caseId}/statistics/{statisticId} + method: delete + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer diff --git a/web-api/serverless-case-notes.yml b/web-api/serverless-case-notes.yml index a00ddc78a2e..0d1d3ac3235 100644 --- a/web-api/serverless-case-notes.yml +++ b/web-api/serverless-case-notes.yml @@ -1,75 +1,14 @@ service: ef-cms-case-notes-${opt:stageColor} plugins: - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3010 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'case-notes' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):case-notes} provider: name: aws @@ -96,21 +35,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -120,39 +45,7 @@ package: excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: getUserCaseNoteForCases: diff --git a/web-api/serverless-case-parties.yml b/web-api/serverless-case-parties.yml index 445329b331b..0168840851e 100644 --- a/web-api/serverless-case-parties.yml +++ b/web-api/serverless-case-parties.yml @@ -1,76 +1,14 @@ service: ef-cms-case-parties-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3014 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'case-parties' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):case-parties} provider: name: aws @@ -98,23 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -123,39 +45,7 @@ package: - web-api/${self:provider.dir}/casePartiesHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: updatePrimaryContact: diff --git a/web-api/serverless-cases.yml b/web-api/serverless-cases.yml index c199dc57d8f..564c939e108 100644 --- a/web-api/serverless-cases.yml +++ b/web-api/serverless-cases.yml @@ -1,76 +1,14 @@ service: ef-cms-cases-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3002 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'cases' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):cases} provider: name: aws @@ -98,24 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EMAIL_SERVED_PETITION_TEMPLATE: petition_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -124,39 +45,7 @@ package: - web-api/${self:provider.dir}/casesHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: saveCaseDetailInternalEdit: @@ -222,6 +111,28 @@ functions: arn: arn:aws:lambda:${opt:region}:${opt:accountId}:function:cognito_authorizer_lambda_${opt:stage} managedExternally: true + getOpenConsolidatedCases: + handler: web-api/${self:provider.dir}/casesHandlers.getOpenConsolidatedCasesLambda + events: + - http: + path: /open + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + arn: arn:aws:lambda:${opt:region}:${opt:accountId}:function:cognito_authorizer_lambda_${opt:stage} + managedExternally: true + + getClosedCases: + handler: web-api/${self:provider.dir}/casesHandlers.getClosedCasesLambda + events: + - http: + path: /closed + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + arn: arn:aws:lambda:${opt:region}:${opt:accountId}:function:cognito_authorizer_lambda_${opt:stage} + managedExternally: true + caseAdvancedSearch: handler: web-api/${self:provider.dir}/casesHandlers.caseAdvancedSearchLambda events: diff --git a/web-api/serverless-documents.yml b/web-api/serverless-documents.yml index 7f4b6aced98..8fe12cd9dee 100644 --- a/web-api/serverless-documents.yml +++ b/web-api/serverless-documents.yml @@ -1,76 +1,14 @@ service: ef-cms-documents-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3004 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'documents' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):documents} provider: name: aws @@ -97,19 +35,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -119,39 +45,7 @@ package: excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: validate: diff --git a/web-api/serverless-messages.yml b/web-api/serverless-messages.yml new file mode 100644 index 00000000000..69d4d746610 --- /dev/null +++ b/web-api/serverless-messages.yml @@ -0,0 +1,121 @@ +service: ef-cms-messages-${opt:stageColor} +plugins: + - serverless-domain-manager + - serverless-offline + - serverless-prune-plugin + - serverless-plugin-tracing + - serverless-latest-layer-version + - serverless-jetpack + - serverless-log-forwarding + +custom: ${file(./web-api/config/custom.yml):messages} + +provider: + name: aws + endpointType: REGIONAL + tracing: true + stage: ${self:custom.stage} + region: ${self:custom.region} + runtime: nodejs12.x + memorySize: 768 + timeout: 30 + logRetentionInDays: 7 + role: arn:aws:iam::${opt:accountId}:role/lambda_role_${opt:stage} + dir: ${opt:run_dir, 'src'} + s3Endpoint: s3.us-east-1.amazonaws.com + dynamodbEndpoint: dynamodb.${opt:region}.amazonaws.com + elasticsearchEndpoint: ${opt:elasticsearch_endpoint} + masterRegion: us-east-1 + userPoolId: us-east-1_7uRkF0Axn + masterDynamodbEndpoint: dynamodb.us-east-1.amazonaws.com + deploymentBucket: + name: ${env:SLS_DEPLOYMENT_BUCKET} + serverSideEncryption: AES256 + apiGateway: + binaryMediaTypes: + - 'application/pdf' + clamavDefDir: /opt/var/lib/clamav + + environment: ${file(./web-api/config/environment-variables.yml)} + +package: + exclude: + - ./** + include: + - web-api/${self:provider.dir}/messagesHandlers.js + excludeDevDependencies: true + +resources: ${file(./web-api/config/resources.yml)} + +functions: + createCaseMessage: + handler: web-api/${self:provider.dir}/messagesHandlers.createCaseMessageLambda + events: + - http: + path: / + method: post + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + getCaseMessage: + handler: web-api/${self:provider.dir}/messagesHandlers.getCaseMessageLambda + events: + - http: + path: /{messageId} + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + getInboxCaseMessagesForUser: + handler: web-api/${self:provider.dir}/messagesHandlers.getInboxCaseMessagesForUserLambda + events: + - http: + path: /inbox/{userId} + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + getInboxCaseMessagesForSection: + handler: web-api/${self:provider.dir}/messagesHandlers.getInboxCaseMessagesForSectionLambda + events: + - http: + path: /inbox/section/{section} + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + getOutboxCaseMessagesForUser: + handler: web-api/${self:provider.dir}/messagesHandlers.getOutboxCaseMessagesForUserLambda + events: + - http: + path: /outbox/{userId} + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer + + getOutboxCaseMessagesForSection: + handler: web-api/${self:provider.dir}/messagesHandlers.getOutboxCaseMessagesForSectionLambda + events: + - http: + path: /outbox/section/{section} + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} + authorizer: + type: COGNITO_USER_POOLS + authorizerId: + Ref: ApiGatewayAuthorizer diff --git a/web-api/serverless-migrate.yml b/web-api/serverless-migrate.yml index 9e283411891..1933992d22f 100644 --- a/web-api/serverless-migrate.yml +++ b/web-api/serverless-migrate.yml @@ -1,8 +1,6 @@ service: ef-cms-migrate plugins: - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing @@ -10,65 +8,7 @@ plugins: - serverless-jetpack - serverless-log-forwarding -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3030 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - notifications: - - protocol: email - endpoint: ustaxcourt@flexion.us - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'migrate' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):migrate} provider: name: aws @@ -96,19 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -117,39 +45,7 @@ package: - web-api/${self:provider.dir}/migrateHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: migrateCase: diff --git a/web-api/serverless-notifications.yml b/web-api/serverless-notifications.yml index 43af30e4211..001490fa002 100644 --- a/web-api/serverless-notifications.yml +++ b/web-api/serverless-notifications.yml @@ -1,10 +1,7 @@ service: ef-cms-notifications plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - # - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing @@ -12,64 +9,7 @@ plugins: - serverless-jetpack - serverless-log-forwarding -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3020 # this port really isn't used, but needs to be unique to prevent address in use errors - websocketPort: 3011 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - websocket: - domainName: efcms-${self:provider.stage}-ws.${opt:domain} - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}-ws.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):notifications} provider: name: aws @@ -97,18 +37,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:custom.vars.masterRegion, self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: diff --git a/web-api/serverless-practitioners.yml b/web-api/serverless-practitioners.yml index f9d1d857c87..333868f05eb 100644 --- a/web-api/serverless-practitioners.yml +++ b/web-api/serverless-practitioners.yml @@ -1,76 +1,14 @@ service: ef-cms-practitioners-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3017 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'practitioners' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):practitioners} provider: name: aws @@ -98,22 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -122,39 +45,7 @@ package: - web-api/${self:provider.dir}/practitionersHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: getPractitionersByName: diff --git a/web-api/serverless-public-api.yml b/web-api/serverless-public-api.yml index 8a5e0ad3add..47f457b3dfa 100644 --- a/web-api/serverless-public-api.yml +++ b/web-api/serverless-public-api.yml @@ -2,8 +2,6 @@ service: ef-cms-public-${opt:stageColor} plugins: - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing @@ -11,62 +9,7 @@ plugins: - serverless-jetpack - serverless-log-forwarding -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3013 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'public-api' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):public-api} provider: name: aws @@ -94,19 +37,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -115,25 +46,7 @@ package: - web-api/${self:provider.dir}/publicApiHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: casePublicSearch: @@ -152,6 +65,14 @@ functions: method: get cors: ui-public-${self:provider.stage}.ef-cms.${opt:domain} + getCaseForPublicDocketSearch: + handler: web-api/${self:provider.dir}/publicApiHandlers.getCaseForPublicDocketSearch + events: + - http: + path: /docket-number-search/{docketNumber} + method: get + cors: ui-public-${self:provider.stage}.ef-cms.${opt:domain} + getPublicDocumentDownloadUrl: handler: web-api/${self:provider.dir}/publicApiHandlers.getPublicDocumentDownloadUrlLambda events: @@ -195,3 +116,11 @@ functions: path: /opinion-search method: get cors: ui-public-${self:provider.stage}.ef-cms.${opt:domain} + + todaysOpinions: + handler: web-api/${self:provider.dir}/publicApiHandlers.todaysOpinionsLambda + events: + - http: + path: /todays-opinions + method: get + cors: ui-${self:provider.stage}.ef-cms.${opt:domain} diff --git a/web-api/serverless-reports.yml b/web-api/serverless-reports.yml index 57b61e3ac0b..76b265676ba 100644 --- a/web-api/serverless-reports.yml +++ b/web-api/serverless-reports.yml @@ -1,75 +1,14 @@ service: ef-cms-reports-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - host: 0.0.0.0 - port: 3016 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'reports' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):reports} provider: name: aws @@ -97,22 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -121,39 +45,7 @@ package: - web-api/${self:provider.dir}/reportsHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: getBlockedCases: diff --git a/web-api/serverless-sections.yml b/web-api/serverless-sections.yml index 7d948d97472..dcd575f6b16 100644 --- a/web-api/serverless-sections.yml +++ b/web-api/serverless-sections.yml @@ -1,76 +1,14 @@ service: ef-cms-sections-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3006 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'sections' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):sections} provider: name: aws @@ -97,18 +35,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -118,39 +45,7 @@ package: excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: getInboxMessagesForSection: diff --git a/web-api/serverless-streams.yml b/web-api/serverless-streams.yml index cc7a6ce522d..e10e1decf59 100644 --- a/web-api/serverless-streams.yml +++ b/web-api/serverless-streams.yml @@ -8,67 +8,7 @@ plugins: - serverless-jetpack - serverless-log-forwarding -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3012 - - serverless-offline-dynamodb-streams: - apiVersion: '2013-12-02' - endpoint: http://0.0.0.0:8000 - region: us-east-1 - accessKeyId: root - secretAccessKey: root - readInterval: 500 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'streams' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):streams} provider: name: aws @@ -96,22 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - ELASTICSEARCH_ENDPOINT: ${self:custom.vars.elasticsearchEndpoint, self:provider.elasticsearchEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EFCMS_DOMAIN: ${opt:domain} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: diff --git a/web-api/serverless-trial-sessions.yml b/web-api/serverless-trial-sessions.yml index 72e7007d6ff..edb628271b6 100644 --- a/web-api/serverless-trial-sessions.yml +++ b/web-api/serverless-trial-sessions.yml @@ -1,76 +1,14 @@ service: ef-cms-trial-sessions-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3007 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'trial-sessions' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):trial-sessions} provider: name: aws @@ -98,21 +36,7 @@ provider: - 'application/zip' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -122,39 +46,7 @@ package: excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: getTrialSessions: diff --git a/web-api/serverless-users.yml b/web-api/serverless-users.yml index cc02948a9f6..d05ac2789ae 100644 --- a/web-api/serverless-users.yml +++ b/web-api/serverless-users.yml @@ -1,76 +1,14 @@ service: ef-cms-users-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3003 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'users' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):users} provider: name: aws @@ -98,22 +36,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - USER_POOL_IRS_ID: ${opt:userPoolIrsId, self:provider.userPoolIrsId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - EMAIL_DOCUMENT_SERVED_TEMPLATE: document_served_${opt:stage} - EMAIL_SOURCE: noreply@mail.efcms-${opt:stage}.${opt:domain} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} - IRS_SUPERUSER_EMAIL: ${opt:irsSuperuserEmail, self:custom.vars.irsSuperuserEmail} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -122,39 +45,7 @@ package: - web-api/${self:provider.dir}/usersHandlers.js excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: getUser: @@ -216,18 +107,6 @@ functions: authorizerId: Ref: ApiGatewayAuthorizer - getConsolidatedCasesByUser: - handler: web-api/${self:provider.dir}/usersHandlers.getConsolidatedCasesByUserLambda - events: - - http: - path: /{userId}/cases-with-consolidation - method: get - cors: ui-${self:provider.stage}.ef-cms.${opt:domain} - authorizer: - type: COGNITO_USER_POOLS - authorizerId: - Ref: ApiGatewayAuthorizer - verifyPendingCaseForUser: handler: web-api/${self:provider.dir}/usersHandlers.verifyPendingCaseForUserLambda events: diff --git a/web-api/serverless-work-items.yml b/web-api/serverless-work-items.yml index 998eee35b96..70b5be5fa50 100644 --- a/web-api/serverless-work-items.yml +++ b/web-api/serverless-work-items.yml @@ -1,76 +1,14 @@ service: ef-cms-work-items-${opt:stageColor} plugins: - # - serverless-plugin-split-stacks - serverless-domain-manager - - serverless-plugin-bind-deployment-id - # - serverless-plugin-aws-alerts - serverless-offline - serverless-prune-plugin - serverless-plugin-tracing - serverless-latest-layer-version - serverless-jetpack - serverless-log-forwarding - # - serverless-plugin-warmup -custom: - logForwarding: - destinationARN: arn:aws:lambda:${opt:region}:${opt:accountId}:function:log_forwarder_${opt:stage} - filterPattern: 'ERROR' - #warmup: - #enabled: true - - jetpack: - mode: npm - lockfile: ../package-lock.json - - prune: - automatic: true - number: 3 - - serverless-offline: - skipCacheInvalidation: ${opt:skipCacheInvalidation} - host: 0.0.0.0 - port: 3005 - - alerts: - stages: - - dev - - stg - - test - - prod - dashboards: true - alarms: - - errorExceptions - definitions: - errorExceptions: - description: 'Internal Error Exception Alarm' - namespace: 'AWS/Lambda' - metric: Errors - threshold: 1 - statistic: Sum - period: 60 - evaluationPeriods: 1 - comparisonOperator: GreaterThanThreshold - pattern: 'ERROR' - topics: - alarm: - topic: arn:aws:sns:${opt:region}:${opt:accountId}:serverless-alerts-topic-${self:provider.stage} - - customDomain: - domainName: efcms-${self:provider.stage}.${opt:domain} - basePath: 'work-items' - endpointType: 'regional' - certificateName: efcms-${self:provider.stage}.${opt:domain} - certificateRegion: ${opt:region} - stage: ${self:provider.stage} - createRoute53Record: false - enabled: true - splitStacks: - perFunction: true - perType: true - stage: ${opt:stage, 'dev'} - region: ${opt:region, 'us-east-1'} - vars: ${file(./config/${self:custom.stage}.yml)} +custom: ${file(./web-api/config/custom.yml):work-items} provider: name: aws @@ -97,18 +35,7 @@ provider: - 'application/pdf' clamavDefDir: /opt/var/lib/clamav - environment: - S3_ENDPOINT: ${self:custom.vars.s3Endpoint, self:provider.s3Endpoint} - DOCUMENTS_BUCKET_NAME: ${opt:domain}-documents-${opt:stage}-us-east-1 - TEMP_DOCUMENTS_BUCKET_NAME: ${opt:domain}-temp-documents-${opt:stage}-us-east-1 - DYNAMODB_ENDPOINT: ${self:custom.vars.dynamodbEndpoint, self:provider.dynamodbEndpoint} - MASTER_DYNAMODB_ENDPOINT: ${self:custom.vars.masterDynamodbEndpoint, self:provider.masterDynamodbEndpoint} - MASTER_REGION: ${self:provider.masterRegion} - STAGE: ${self:custom.stage} - USER_POOL_ID: ${opt:userPoolId, self:provider.userPoolId} - NODE_ENV: ${self:custom.vars.nodeEnv} - CLAMAV_DEF_DIR: ${self:custom.vars.clamavDefDir, self:provider.clamavDefDir} - CIRCLE_HONEYBADGER_API_KEY: ${opt:circleHoneybadgerApiKey} + environment: ${file(./web-api/config/environment-variables.yml)} package: exclude: @@ -118,39 +45,7 @@ package: excludeDevDependencies: true -resources: - Resources: - ##### - # This Authorizer is used for validating the JWT token before invoking the lambda - ##### - ApiGatewayAuthorizer: - Type: AWS::ApiGateway::Authorizer - Properties: - Name: CognitoUserPool - Type: COGNITO_USER_POOLS - IdentitySource: method.request.header.Authorization - RestApiId: - Ref: ApiGatewayRestApi - ProviderARNs: - - arn:aws:cognito-idp:${opt:region}:${opt:accountId}:userpool/${self:provider.environment.USER_POOL_ID} - - ##### - # Begin Stage for API Gateway Logging - ##### - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - Properties: - DeploymentId: - Ref: __deployment__ - RestApiId: - Ref: ApiGatewayRestApi - StageName: ${opt:stage} - MethodSettings: - - DataTraceEnabled: true - HttpMethod: '*' - LoggingLevel: INFO - ResourcePath: '/*' - MetricsEnabled: true +resources: ${file(./web-api/config/resources.yml)} functions: assignWorkItems: diff --git a/web-api/setup-court-users.sh b/web-api/setup-court-users.sh index 027974d2672..331a692dd87 100755 --- a/web-api/setup-court-users.sh +++ b/web-api/setup-court-users.sh @@ -42,6 +42,8 @@ createAccount() { judgeFullName=$5 judgeTitle=$6 + node ../shared/src/tools/validateUser.js "${email}" "${role}" "${section}" "${name}" "${judgeFullName}" "${judgeTitle}" + curl --header "Content-Type: application/json" \ --header "Authorization: Bearer ${adminToken}" \ --request POST \ diff --git a/web-api/src/applicationContext.js b/web-api/src/applicationContext.js index 81608ca2f34..5b211bf5b25 100644 --- a/web-api/src/applicationContext.js +++ b/web-api/src/applicationContext.js @@ -7,13 +7,11 @@ const AWS = ? AWSXRay.captureAWS(require('aws-sdk')) : require('aws-sdk'); -const { getUniqueId } = require('../../shared/src/sharedAppContext.js'); - const barNumberGenerator = require('../../shared/src/persistence/dynamo/users/barNumberGenerator'); const connectionClass = require('http-aws-es'); const docketNumberGenerator = require('../../shared/src/persistence/dynamo/cases/docketNumberGenerator'); const elasticsearch = require('elasticsearch'); -const elasticsearchIndexes = require('../elasticsearch/elasticsearch-indexes'); +const pdfLib = require('pdf-lib'); const util = require('util'); const { addCaseToTrialSessionInteractor, @@ -24,6 +22,25 @@ const { const { addCoversheetInteractor, } = require('../../shared/src/business/useCases/addCoversheetInteractor'); +const { + addDeficiencyStatisticInteractor, +} = require('../../shared/src/business/useCases/caseStatistics/addDeficiencyStatisticInteractor'); +const { + addressLabelCoverSheet, + caseInventoryReport, + changeOfAddress, + coverSheet, + docketRecord, + noticeOfDocketChange, + noticeOfReceiptOfPetition, + order, + pendingReport, + receiptOfFiling, + standingPretrialNotice, + standingPretrialOrder, + trialCalendar, + trialSessionPlanningReport, +} = require('../../shared/src/business/utilities/documentGenerators'); const { addWorkItemToSectionInbox, } = require('../../shared/src/persistence/dynamo/workitems/addWorkItemToSectionInbox'); @@ -60,21 +77,16 @@ const { const { bulkIndexRecords, } = require('../../shared/src/persistence/elasticsearch/bulkIndexRecords'); +const { + CASE_STATUS_TYPES, + SESSION_STATUS_GROUPS, +} = require('../../shared/src/business/entities/EntityConstants'); const { caseAdvancedSearch, } = require('../../shared/src/persistence/elasticsearch/caseAdvancedSearch'); const { caseAdvancedSearchInteractor, } = require('../../shared/src/business/useCases/caseAdvancedSearchInteractor'); -const { - caseInventoryReport, - changeOfAddress, - docketRecord, - noticeOfDocketChange, - pendingReport, - receiptOfFiling, - standingPretrialOrder, -} = require('../../shared/src/business/utilities/documentGenerators'); const { casePublicSearch: casePublicSearchPersistence, } = require('../../shared/src/persistence/elasticsearch/casePublicSearch'); @@ -120,6 +132,12 @@ const { const { createCaseInteractor, } = require('../../shared/src/business/useCases/createCaseInteractor'); +const { + createCaseMessage, +} = require('../../shared/src/persistence/dynamo/messages/createCaseMessage'); +const { + createCaseMessageInteractor, +} = require('../../shared/src/business/useCases/messages/createCaseMessageInteractor'); const { createCaseTrialSortMappingRecords, } = require('../../shared/src/persistence/dynamo/cases/createCaseTrialSortMappingRecords'); @@ -168,6 +186,9 @@ const { const { createWorkItemInteractor, } = require('../../shared/src/business/useCases/workitems/createWorkItemInteractor'); +const { + deleteCaseCorrespondence, +} = require('../../shared/src/persistence/dynamo/correspondence/deleteCaseCorrespondence'); const { deleteCaseDeadline, } = require('../../shared/src/persistence/dynamo/caseDeadlines/deleteCaseDeadline'); @@ -180,9 +201,15 @@ const { const { deleteCaseTrialSortMappingRecords, } = require('../../shared/src/persistence/dynamo/cases/deleteCaseTrialSortMappingRecords'); +const { + deleteCorrespondenceDocumentInteractor, +} = require('../../shared/src/business/useCases/correspondence/deleteCorrespondenceDocumentInteractor'); const { deleteCounselFromCaseInteractor, } = require('../../shared/src/business/useCases/caseAssociation/deleteCounselFromCaseInteractor'); +const { + deleteDeficiencyStatisticInteractor, +} = require('../../shared/src/business/useCases/caseStatistics/deleteDeficiencyStatisticInteractor'); const { deleteDocument, } = require('../../shared/src/persistence/s3/deleteDocument'); @@ -222,6 +249,9 @@ const { const { deleteWorkItemFromSection, } = require('../../shared/src/persistence/dynamo/workitems/deleteWorkItemFromSection'); +const { + elasticsearchIndexes, +} = require('../elasticsearch/elasticsearch-indexes'); const { fetchPendingItems, } = require('../../shared/src/business/useCaseHelper/pendingItems/fetchPendingItems'); @@ -252,12 +282,15 @@ const { const { fileExternalDocumentInteractor, } = require('../../shared/src/business/useCases/externalDocument/fileExternalDocumentInteractor'); +const { + formatAndSortConsolidatedCases, +} = require('../../shared/src/business/useCaseHelper/consolidatedCases/formatAndSortConsolidatedCases'); +const { + formatJudgeName, +} = require('../../shared/src/business/utilities/getFormattedJudgeName'); const { forwardWorkItemInteractor, } = require('../../shared/src/business/useCases/workitems/forwardWorkItemInteractor'); -const { - generateCaseConfirmationPdf, -} = require('../../shared/src/business/useCaseHelper/caseConfirmation/generateCaseConfirmationPdf'); const { generateCaseInventoryReportPdf, } = require('../../shared/src/business/useCaseHelper/caseInventoryReport/generateCaseInventoryReportPdf'); @@ -265,8 +298,6 @@ const { generateChangeOfAddressTemplate, generateNoticeOfTrialIssuedTemplate, generatePrintableDocketRecordTemplate, - generateTrialCalendarTemplate, - generateTrialSessionPlanningReportTemplate, } = require('../../shared/src/business/utilities/generateHTMLTemplateForPDF/'); const { generateDocketRecordPdfInteractor, @@ -274,9 +305,6 @@ const { const { generateNoticeOfTrialIssuedInteractor, } = require('../../shared/src/business/useCases/trialSessions/generateNoticeOfTrialIssuedInteractor'); -const { - generatePaperServiceAddressPagePdf, -} = require('../../shared/src/business/useCaseHelper/courtIssuedDocument/generatePaperServiceAddressPagePdf'); const { generatePdfFromHtmlInteractor, } = require('../../shared/src/business/useCases/generatePdfFromHtmlInteractor'); @@ -295,9 +323,6 @@ const { const { generateStandingPretrialNoticeInteractor, } = require('../../shared/src/business/useCases/trialSessions/generateStandingPretrialNoticeInteractor'); -const { - generateStandingPretrialNoticeTemplate, -} = require('../../shared/src/business/useCaseHelper/standingPretrialNotice/generateStandingPretrialNoticeTemplate'); const { generateStandingPretrialOrderInteractor, } = require('../../shared/src/business/useCases/trialSessions/generateStandingPretrialOrderInteractor'); @@ -337,6 +362,9 @@ const { const { getCaseDeadlinesForCaseInteractor, } = require('../../shared/src/business/useCases/caseDeadline/getCaseDeadlinesForCaseInteractor'); +const { + getCaseForPublicDocketSearchInteractor, +} = require('../../shared/src/business/useCases/public/getCaseForPublicDocketSearchInteractor'); const { getCaseInteractor, } = require('../../shared/src/business/useCases/getCaseInteractor'); @@ -346,6 +374,12 @@ const { const { getCaseInventoryReportInteractor, } = require('../../shared/src/business/useCases/caseInventoryReport/getCaseInventoryReportInteractor'); +const { + getCaseMessageById, +} = require('../../shared/src/persistence/dynamo/messages/getCaseMessageById'); +const { + getCaseMessageInteractor, +} = require('../../shared/src/business/useCases/messages/getCaseMessageInteractor'); const { getCasesByCaseIds, } = require('../../shared/src/persistence/dynamo/cases/getCasesByCaseIds'); @@ -361,12 +395,18 @@ const { const { getChromiumBrowser, } = require('../../shared/src/business/utilities/getChromiumBrowser'); +const { + getClosedCasesByUser, +} = require('../../shared/src/persistence/dynamo/cases/getClosedCasesByUser'); +const { + getClosedCasesInteractor, +} = require('../../shared/src/business/useCases/getClosedCasesInteractor'); const { getConsolidatedCasesByCaseInteractor, } = require('../../shared/src/business/useCases/getConsolidatedCasesByCaseInteractor'); const { - getConsolidatedCasesByUserInteractor, -} = require('../../shared/src/business/useCases/getConsolidatedCasesByUserInteractor'); + getConsolidatedCasesForLeadCase, +} = require('../../shared/src/business/useCaseHelper/consolidatedCases/getConsolidatedCasesForLeadCase'); const { getDocumentQCInboxForSection, } = require('../../shared/src/persistence/dynamo/workitems/getDocumentQCInboxForSection'); @@ -415,6 +455,12 @@ const { const { getFormattedCaseDetail, } = require('../../shared/src/business/utilities/getFormattedCaseDetail'); +const { + getInboxCaseMessagesForSectionInteractor, +} = require('../../shared/src/business/useCases/messages/getInboxCaseMessagesForSectionInteractor'); +const { + getInboxCaseMessagesForUserInteractor, +} = require('../../shared/src/business/useCases/messages/getInboxCaseMessagesForUserInteractor'); const { getInboxMessagesForSection, } = require('../../shared/src/persistence/dynamo/workitems/getInboxMessagesForSection'); @@ -427,6 +473,9 @@ const { const { getInboxMessagesForUserInteractor, } = require('../../shared/src/business/useCases/workitems/getInboxMessagesForUserInteractor'); +const { + getIndexedCasesForUser, +} = require('../../shared/src/persistence/elasticsearch/getIndexedCasesForUser'); const { getIndexMappingFields, } = require('../../shared/src/persistence/elasticsearch/getIndexMappingFields'); @@ -451,6 +500,18 @@ const { const { getNotificationsInteractor, } = require('../../shared/src/business/useCases/getNotificationsInteractor'); +const { + getOpenCasesByUser, +} = require('../../shared/src/persistence/dynamo/cases/getOpenCasesByUser'); +const { + getOpenConsolidatedCasesInteractor, +} = require('../../shared/src/business/useCases/getOpenConsolidatedCasesInteractor'); +const { + getOutboxCaseMessagesForSectionInteractor, +} = require('../../shared/src/business/useCases/messages/getOutboxCaseMessagesForSectionInteractor'); +const { + getOutboxCaseMessagesForUserInteractor, +} = require('../../shared/src/business/useCases/messages/getOutboxCaseMessagesForUserInteractor'); const { getPractitionerByBarNumber, } = require('../../shared/src/persistence/dynamo/users/getPractitionerByBarNumber'); @@ -478,6 +539,12 @@ const { const { getRecord, } = require('../../shared/src/persistence/dynamo/elasticsearch/getRecord'); +const { + getSectionInboxMessages, +} = require('../../shared/src/persistence/elasticsearch/messages/getSectionInboxMessages'); +const { + getSectionOutboxMessages, +} = require('../../shared/src/persistence/elasticsearch/messages/getSectionOutboxMessages'); const { getSentMessagesForSection, } = require('../../shared/src/persistence/dynamo/workitems/getSentMessagesForSection'); @@ -490,6 +557,9 @@ const { const { getSentMessagesForUserInteractor, } = require('../../shared/src/business/useCases/workitems/getSentMessagesForUserInteractor'); +const { + getTodaysOpinionsInteractor, +} = require('../../shared/src/business/useCases/public/getTodaysOpinionsInteractor'); const { getTrialSessionById, } = require('../../shared/src/persistence/dynamo/trialSessions/getTrialSessionById'); @@ -508,6 +578,9 @@ const { const { getTrialSessionWorkingCopyInteractor, } = require('../../shared/src/business/useCases/trialSessions/getTrialSessionWorkingCopyInteractor'); +const { + getUnassociatedLeadCase, +} = require('../../shared/src/business/useCaseHelper/consolidatedCases/getUnassociatedLeadCase'); const { getUploadPolicy, } = require('../../shared/src/persistence/s3/getUploadPolicy'); @@ -532,9 +605,18 @@ const { const { getUserCaseNoteInteractor, } = require('../../shared/src/business/useCases/caseNote/getUserCaseNoteInteractor'); +const { + getUserCases, +} = require('../../shared/src/persistence/dynamo/cases/getUserCases'); +const { + getUserInboxMessages, +} = require('../../shared/src/persistence/elasticsearch/messages/getUserInboxMessages'); const { getUserInteractor, } = require('../../shared/src/business/useCases/getUserInteractor'); +const { + getUserOutboxMessages, +} = require('../../shared/src/persistence/elasticsearch/messages/getUserOutboxMessages'); const { getUsersBySearchKey, } = require('../../shared/src/persistence/dynamo/users/getUsersBySearchKey'); @@ -587,6 +669,9 @@ const { const { opinionPublicSearchInteractor, } = require('../../shared/src/business/useCases/public/opinionPublicSearchInteractor'); +const { + ORDER_TYPES, +} = require('../../shared/src/business/entities/EntityConstants'); const { orderAdvancedSearchInteractor, } = require('../../shared/src/business/useCases/orderAdvancedSearchInteractor'); @@ -605,6 +690,9 @@ const { const { processStreamRecordsInteractor, } = require('../../shared/src/business/useCases/processStreamRecordsInteractor'); +const { + processUserAssociatedCases, +} = require('../../shared/src/business/useCaseHelper/consolidatedCases/processUserAssociatedCases'); const { putWorkItemInOutbox, } = require('../../shared/src/persistence/dynamo/workitems/putWorkItemInOutbox'); @@ -707,9 +795,6 @@ const { const { submitPendingCaseAssociationRequestInteractor, } = require('../../shared/src/business/useCases/caseAssociationRequest/submitPendingCaseAssociationRequestInteractor'); -const { - TrialSession, -} = require('../../shared/src/business/entities/trialSessions/TrialSession'); const { unblockCaseFromTrialInteractor, } = require('../../shared/src/business/useCases/unblockCaseFromTrialInteractor'); @@ -737,6 +822,9 @@ const { const { updateCaseTrialSortTagsInteractor, } = require('../../shared/src/business/useCases/updateCaseTrialSortTagsInteractor'); +const { + updateCorrespondenceDocumentInteractor, +} = require('../../shared/src/business/useCases/correspondence/updateCorrespondenceDocumentInteractor'); const { updateCounselOnCaseInteractor, } = require('../../shared/src/business/useCases/caseAssociation/updateCounselOnCaseInteractor'); @@ -746,18 +834,30 @@ const { const { updateCourtIssuedOrderInteractor, } = require('../../shared/src/business/useCases/courtIssuedOrder/updateCourtIssuedOrderInteractor'); +const { + updateDeficiencyStatisticInteractor, +} = require('../../shared/src/business/useCases/caseStatistics/updateDeficiencyStatisticInteractor'); const { updateDocketEntryInteractor, } = require('../../shared/src/business/useCases/docketEntry/updateDocketEntryInteractor'); const { updateDocketEntryMetaInteractor, } = require('../../shared/src/business/useCases/docketEntry/updateDocketEntryMetaInteractor'); +const { + updateDocketRecord, +} = require('../../shared/src/persistence/dynamo/docketRecord/updateDocketRecord'); +const { + updateDocument, +} = require('../../shared/src/persistence/dynamo/documents/updateDocument'); const { updateDocumentProcessingStatus, } = require('../../shared/src/persistence/dynamo/documents/updateDocumentProcessingStatus'); const { updateHighPriorityCaseTrialSortMappingRecords, } = require('../../shared/src/persistence/dynamo/cases/updateHighPriorityCaseTrialSortMappingRecords'); +const { + updateOtherStatisticsInteractor, +} = require('../../shared/src/business/useCases/caseStatistics/updateOtherStatisticsInteractor'); const { updatePetitionDetailsInteractor, } = require('../../shared/src/business/useCases/updatePetitionDetailsInteractor'); @@ -834,9 +934,8 @@ const { Case } = require('../../shared/src/business/entities/cases/Case'); const { Document } = require('../../shared/src/business/entities/Document'); const { exec } = require('child_process'); const { getDocument } = require('../../shared/src/persistence/s3/getDocument'); -const { Order } = require('../../shared/src/business/entities/orders/Order'); +const { getUniqueId } = require('../../shared/src/sharedAppContext.js'); const { User } = require('../../shared/src/business/entities/User'); - const { v4: uuidv4 } = require('uuid'); // increase the timeout for zip uploads to S3 @@ -952,8 +1051,11 @@ module.exports = (appContextUser = {}) => { }, getConstants: () => ({ CASE_INVENTORY_MAX_PAGE_SIZE: 5000, - ORDER_TYPES_MAP: Order.ORDER_TYPES, - SESSION_STATUS_GROUPS: TrialSession.SESSION_STATUS_GROUPS, + OPEN_CASE_STATUSES: Object.values(CASE_STATUS_TYPES).filter( + status => status !== CASE_STATUS_TYPES.closed, + ), + ORDER_TYPES_MAP: ORDER_TYPES, + SESSION_STATUS_GROUPS, }), getCurrentUser, getDispatchers: () => ({ @@ -961,13 +1063,20 @@ module.exports = (appContextUser = {}) => { }), getDocumentClient, getDocumentGenerators: () => ({ + addressLabelCoverSheet, caseInventoryReport, changeOfAddress, + coverSheet, docketRecord, noticeOfDocketChange, + noticeOfReceiptOfPetition, + order, pendingReport, receiptOfFiling, + standingPretrialNotice, standingPretrialOrder, + trialCalendar, + trialSessionPlanningReport, }), getDocumentsBucketName: () => { return environment.documentsBucketName; @@ -1041,6 +1150,9 @@ module.exports = (appContextUser = {}) => { pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js'; return pdfjsLib; }, + getPdfLib: () => { + return pdfLib; + }, getPersistenceGateway: () => { return { addWorkItemToSectionInbox, @@ -1053,6 +1165,7 @@ module.exports = (appContextUser = {}) => { createCase, createCaseCatalogRecord, createCaseDeadline, + createCaseMessage, createCaseTrialSortMappingRecords, createElasticsearchReindexRecord, createPractitionerUser, @@ -1062,9 +1175,10 @@ module.exports = (appContextUser = {}) => { createUser, createUserInboxRecord, createWorkItem, + deleteCaseCorrespondence, deleteCaseDeadline, deleteCaseTrialSortMappingRecords, - deleteDocument: args => (process.env.CI ? null : deleteDocument(args)), + deleteDocument, deleteElasticsearchReindexRecord, deleteSectionOutboxRecord, deleteTrialSession, @@ -1085,9 +1199,11 @@ module.exports = (appContextUser = {}) => { getCaseByDocketNumber, getCaseDeadlinesByCaseId, getCaseInventoryReport, + getCaseMessageById, getCasesByCaseIds, getCasesByLeadCaseId, getCasesByUser, + getClosedCasesByUser, getDocument, getDocumentQCInboxForSection, getDocumentQCInboxForUser, @@ -1101,11 +1217,15 @@ module.exports = (appContextUser = {}) => { getInboxMessagesForUser, getIndexMappingFields, getIndexMappingLimit, + getIndexedCasesForUser, getInternalUsers, + getOpenCasesByUser, getPractitionerByBarNumber, getPractitionersByName, getPublicDownloadPolicyUrl, getRecord, + getSectionInboxMessages, + getSectionOutboxMessages, getSentMessagesForSection, getSentMessagesForUser, getTrialSessionById, @@ -1115,6 +1235,9 @@ module.exports = (appContextUser = {}) => { getUserById, getUserCaseNote, getUserCaseNoteForCases, + getUserCases, + getUserInboxMessages, + getUserOutboxMessages, getUsersBySearchKey, getUsersInSection, getWebSocketConnectionByConnectionId, @@ -1136,6 +1259,8 @@ module.exports = (appContextUser = {}) => { updateCase, updateCaseDeadline, updateCaseTrialSortMappingRecords, + updateDocketRecord, + updateDocument, updateDocumentProcessingStatus, updateHighPriorityCaseTrialSortMappingRecords, updatePractitionerUser, @@ -1205,9 +1330,6 @@ module.exports = (appContextUser = {}) => { generateChangeOfAddressTemplate, generateNoticeOfTrialIssuedTemplate, generatePrintableDocketRecordTemplate, - generateStandingPretrialNoticeTemplate, - generateTrialCalendarTemplate, - generateTrialSessionPlanningReportTemplate, }; }, getUniqueId, @@ -1216,10 +1338,12 @@ module.exports = (appContextUser = {}) => { appendPaperServiceAddressPageToPdf, countPagesInDocument, fetchPendingItems, - generateCaseConfirmationPdf, + formatAndSortConsolidatedCases, generateCaseInventoryReportPdf, - generatePaperServiceAddressPagePdf, getCaseInventoryReport, + getConsolidatedCasesForLeadCase, + getUnassociatedLeadCase, + processUserAssociatedCases, saveFileAndGenerateUrl, sendIrsSuperuserPetitionEmail, sendServedPartiesEmails, @@ -1231,6 +1355,7 @@ module.exports = (appContextUser = {}) => { addCaseToTrialSessionInteractor, addConsolidatedCaseInteractor, addCoversheetInteractor, + addDeficiencyStatisticInteractor, archiveDraftDocumentInteractor, assignWorkItemsInteractor, associateIrsPractitionerWithCaseInteractor, @@ -1242,10 +1367,10 @@ module.exports = (appContextUser = {}) => { checkForReadyForTrialCasesInteractor, completeDocketEntryQCInteractor, completeWorkItemInteractor, - createCaseDeadlineInteractor, createCaseFromPaperInteractor, createCaseInteractor, + createCaseMessageInteractor, createCourtIssuedOrderPdfFromHtmlInteractor, createPractitionerUserInteractor, createTrialSessionInteractor, @@ -1253,7 +1378,9 @@ module.exports = (appContextUser = {}) => { createWorkItemInteractor, deleteCaseDeadlineInteractor, deleteCaseNoteInteractor, + deleteCorrespondenceDocumentInteractor, deleteCounselFromCaseInteractor, + deleteDeficiencyStatisticInteractor, deleteTrialSessionInteractor, deleteUserCaseNoteInteractor, fetchPendingItemsInteractor, @@ -1278,17 +1405,21 @@ module.exports = (appContextUser = {}) => { getBlockedCasesInteractor, getCalendaredCasesForTrialSessionInteractor, getCaseDeadlinesForCaseInteractor, + getCaseForPublicDocketSearchInteractor, getCaseInteractor, getCaseInventoryReportInteractor, + getCaseMessageInteractor, getCasesByUserInteractor, + getClosedCasesInteractor, getConsolidatedCasesByCaseInteractor, - getConsolidatedCasesByUserInteractor, getDocumentQCInboxForSectionInteractor, getDocumentQCInboxForUserInteractor, getDocumentQCServedForSectionInteractor, getDocumentQCServedForUserInteractor, getDownloadPolicyUrlInteractor, getEligibleCasesForTrialSessionInteractor, + getInboxCaseMessagesForSectionInteractor, + getInboxCaseMessagesForUserInteractor, getInboxMessagesForSectionInteractor, getInboxMessagesForUserInteractor, getInternalUsersInteractor, @@ -1296,6 +1427,9 @@ module.exports = (appContextUser = {}) => { getJudgeForUserChambersInteractor, getJudgesForPublicSearchInteractor, getNotificationsInteractor, + getOpenConsolidatedCasesInteractor, + getOutboxCaseMessagesForSectionInteractor, + getOutboxCaseMessagesForUserInteractor, getPractitionerByBarNumberInteractor, getPractitionersByNameInteractor, getPrivatePractitionersBySearchKeyInteractor, @@ -1303,6 +1437,7 @@ module.exports = (appContextUser = {}) => { getPublicDownloadPolicyUrlInteractor, getSentMessagesForSectionInteractor, getSentMessagesForUserInteractor, + getTodaysOpinionsInteractor, getTrialSessionDetailsInteractor, getTrialSessionWorkingCopyInteractor, getTrialSessionsInteractor, @@ -1343,11 +1478,14 @@ module.exports = (appContextUser = {}) => { updateCaseContextInteractor, updateCaseDeadlineInteractor, updateCaseTrialSortTagsInteractor, + updateCorrespondenceDocumentInteractor, updateCounselOnCaseInteractor, updateCourtIssuedDocketEntryInteractor, updateCourtIssuedOrderInteractor, + updateDeficiencyStatisticInteractor, updateDocketEntryInteractor, updateDocketEntryMetaInteractor, + updateOtherStatisticsInteractor, updatePetitionDetailsInteractor, updatePetitionerInformationInteractor, updatePractitionerUserInteractor, @@ -1373,6 +1511,7 @@ module.exports = (appContextUser = {}) => { createISODateString, formatCaseForTrialSession, formatDateString, + formatJudgeName, formatNow, formattedTrialSessionDetails, getDocumentTypeForAddressChange, @@ -1389,7 +1528,7 @@ module.exports = (appContextUser = {}) => { logger: { error: value => { // eslint-disable-next-line no-console - console.error(JSON.stringify(value)); + console.error(value); }, info: (key, value) => { // eslint-disable-next-line no-console diff --git a/web-api/src/caseDocumentsHandlers.js b/web-api/src/caseDocumentsHandlers.js index 2f093be2ff2..349b760390d 100644 --- a/web-api/src/caseDocumentsHandlers.js +++ b/web-api/src/caseDocumentsHandlers.js @@ -7,6 +7,8 @@ module.exports = { .completeDocketEntryQCLambda, createWorkItemLambda: require('./workitems/createWorkItemLambda') .createWorkItemLambda, + deleteCorrespondenceDocumentLambda: require('./correspondence/deleteCorrespondenceDocumentLambda') + .deleteCorrespondenceDocumentLambda, downloadPolicyUrlLambda: require('./documents/downloadPolicyUrlLambda') .downloadPolicyUrlLambda, fileCorrespondenceDocumentLambda: require('./correspondence/fileCorrespondenceDocumentLambda') diff --git a/web-api/src/caseMetaHandlers.js b/web-api/src/caseMetaHandlers.js index 401cde0118f..539ec4fd5f4 100644 --- a/web-api/src/caseMetaHandlers.js +++ b/web-api/src/caseMetaHandlers.js @@ -1,8 +1,12 @@ module.exports = { addConsolidatedCaseLambda: require('./cases/addConsolidatedCaseLambda') .addConsolidatedCaseLambda, + addDeficiencyStatisticLambda: require('./cases/addDeficiencyStatisticLambda') + .addDeficiencyStatisticLambda, blockCaseFromTrialLambda: require('./cases/blockCaseFromTrialLambda') .blockCaseFromTrialLambda, + deleteDeficiencyStatisticLambda: require('./cases/deleteDeficiencyStatisticLambda') + .deleteDeficiencyStatisticLambda, prioritizeCaseLambda: require('./cases/prioritizeCaseLambda') .prioritizeCaseLambda, removeConsolidatedCasesLambda: require('./cases/removeConsolidatedCasesLambda') @@ -16,6 +20,10 @@ module.exports = { .updateCaseContextLambda, updateCaseTrialSortTagsLambda: require('./cases/updateCaseTrialSortTagsLambda') .updateCaseTrialSortTagsLambda, + updateDeficiencyStatisticLambda: require('./cases/updateDeficiencyStatisticLambda') + .updateDeficiencyStatisticLambda, + updateOtherStatisticsLambda: require('./cases/updateOtherStatisticsLambda') + .updateOtherStatisticsLambda, updateQcCompleteForTrialLambda: require('./cases/updateQcCompleteForTrialLambda') .updateQcCompleteForTrialLambda, }; diff --git a/web-api/src/cases/addDeficiencyStatisticLambda.js b/web-api/src/cases/addDeficiencyStatisticLambda.js new file mode 100644 index 00000000000..dcf8b3e242d --- /dev/null +++ b/web-api/src/cases/addDeficiencyStatisticLambda.js @@ -0,0 +1,18 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * adds a statistic to the case + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.addDeficiencyStatisticLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .addDeficiencyStatisticInteractor({ + applicationContext, + caseId: event.pathParameters.caseId, + ...JSON.parse(event.body), + }); + }); diff --git a/web-api/src/cases/deleteDeficiencyStatisticLambda.js b/web-api/src/cases/deleteDeficiencyStatisticLambda.js new file mode 100644 index 00000000000..56ee3aa430a --- /dev/null +++ b/web-api/src/cases/deleteDeficiencyStatisticLambda.js @@ -0,0 +1,18 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * deletes a statistic from the case + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.deleteDeficiencyStatisticLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .deleteDeficiencyStatisticInteractor({ + applicationContext, + caseId: event.pathParameters.caseId, + statisticId: event.pathParameters.statisticId, + }); + }); diff --git a/web-api/src/cases/getClosedCasesLambda.js b/web-api/src/cases/getClosedCasesLambda.js new file mode 100644 index 00000000000..d9d8fa10733 --- /dev/null +++ b/web-api/src/cases/getClosedCasesLambda.js @@ -0,0 +1,14 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * used for fetching all closed cases + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.getClosedCasesLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext.getUseCases().getClosedCasesInteractor({ + applicationContext, + }); + }); diff --git a/web-api/src/cases/getOpenConsolidatedCasesLambda.js b/web-api/src/cases/getOpenConsolidatedCasesLambda.js new file mode 100644 index 00000000000..719e0e4ab36 --- /dev/null +++ b/web-api/src/cases/getOpenConsolidatedCasesLambda.js @@ -0,0 +1,16 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * used for fetching all open cases + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.getOpenConsolidatedCasesLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + }); diff --git a/web-api/src/cases/updateDeficiencyStatisticLambda.js b/web-api/src/cases/updateDeficiencyStatisticLambda.js new file mode 100644 index 00000000000..dc358ccabae --- /dev/null +++ b/web-api/src/cases/updateDeficiencyStatisticLambda.js @@ -0,0 +1,19 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * updates a statistic on the case + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.updateDeficiencyStatisticLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .updateDeficiencyStatisticInteractor({ + applicationContext, + caseId: event.pathParameters.caseId, + statisticId: event.pathParameters.statisticId, + ...JSON.parse(event.body), + }); + }); diff --git a/web-api/src/cases/updateOtherStatisticsLambda.js b/web-api/src/cases/updateOtherStatisticsLambda.js new file mode 100644 index 00000000000..f6db19e6e1e --- /dev/null +++ b/web-api/src/cases/updateOtherStatisticsLambda.js @@ -0,0 +1,18 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * updates other statistics on the case (litigation costs and damages) + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.updateOtherStatisticsLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .updateOtherStatisticsInteractor({ + applicationContext, + caseId: event.pathParameters.caseId, + ...JSON.parse(event.body), + }); + }); diff --git a/web-api/src/casesHandlers.js b/web-api/src/casesHandlers.js index 941d4cc3b92..f8454a9b029 100644 --- a/web-api/src/casesHandlers.js +++ b/web-api/src/casesHandlers.js @@ -5,8 +5,12 @@ module.exports = { .createCaseFromPaperLambda, createCaseLambda: require('./cases/createCaseLambda').createCaseLambda, getCaseLambda: require('./cases/getCaseLambda').getCaseLambda, + getClosedCasesLambda: require('./cases/getClosedCasesLambda') + .getClosedCasesLambda, getConsolidatedCasesByCaseLambda: require('./cases/getConsolidatedCasesByCaseLambda') .getConsolidatedCasesByCaseLambda, + getOpenConsolidatedCasesLambda: require('./cases/getOpenConsolidatedCasesLambda') + .getOpenConsolidatedCasesLambda, removeCasePendingItemLambda: require('./cases/removeCasePendingItemLambda') .removeCasePendingItemLambda, saveCaseDetailInternalEditLambda: require('./cases/saveCaseDetailInternalEditLambda') diff --git a/web-api/src/correspondence/deleteCorrespondenceDocumentLambda.js b/web-api/src/correspondence/deleteCorrespondenceDocumentLambda.js new file mode 100644 index 00000000000..6d8e824ebeb --- /dev/null +++ b/web-api/src/correspondence/deleteCorrespondenceDocumentLambda.js @@ -0,0 +1,20 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * deletes a correspondence document + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.deleteCorrespondenceDocumentLambda = event => + genericHandler(event, async ({ applicationContext }) => { + const { caseId, documentId } = event.pathParameters || {}; + + return await applicationContext + .getUseCases() + .deleteCorrespondenceDocumentInteractor({ + applicationContext, + caseId, + documentId, + }); + }); diff --git a/web-api/src/correspondence/updateCorrespondenceDocumentLambda.js b/web-api/src/correspondence/updateCorrespondenceDocumentLambda.js index 691d78420b6..372cb695049 100644 --- a/web-api/src/correspondence/updateCorrespondenceDocumentLambda.js +++ b/web-api/src/correspondence/updateCorrespondenceDocumentLambda.js @@ -1,4 +1,4 @@ -// const { genericHandler } = require('../genericHandler'); +const { genericHandler } = require('../genericHandler'); /** * upload a correspondence document @@ -6,11 +6,12 @@ * @param {object} event the AWS event object * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers */ -// TODO - implement in next step -// exports.updateCorrespondenceDocumentLambda = event => -// genericHandler(event, async ({ applicationContext }) => { -// return await applicationContext.getUseCases().uploadCorrespondenceDocument({ -// ...JSON.parse(event.body), -// applicationContext, -// }); -// }); +exports.updateCorrespondenceDocumentLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .updateCorrespondenceDocumentInteractor({ + ...JSON.parse(event.body), + applicationContext, + }); + }); diff --git a/web-api/src/genericHandler.test.js b/web-api/src/genericHandler.test.js index 3ea4387359a..906c7003fe9 100644 --- a/web-api/src/genericHandler.test.js +++ b/web-api/src/genericHandler.test.js @@ -12,7 +12,10 @@ const MOCK_EVENT = { }, }; -const MOCK_USER = { name: 'Test User', userId: '1' }; +const MOCK_USER = { + name: 'Test User', + userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291', +}; let logged = []; let lastLoggedValue; diff --git a/web-api/src/messages/createCaseMessageLambda.js b/web-api/src/messages/createCaseMessageLambda.js new file mode 100644 index 00000000000..66ef3b0c933 --- /dev/null +++ b/web-api/src/messages/createCaseMessageLambda.js @@ -0,0 +1,15 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * lambda which is used for creating a new case message + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.createCaseMessageLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext.getUseCases().createCaseMessageInteractor({ + ...JSON.parse(event.body), + applicationContext, + }); + }); diff --git a/web-api/src/messages/getCaseMessageLambda.js b/web-api/src/messages/getCaseMessageLambda.js new file mode 100644 index 00000000000..60f0f3ef43e --- /dev/null +++ b/web-api/src/messages/getCaseMessageLambda.js @@ -0,0 +1,15 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * lambda which is used for creating a new case message + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.getCaseMessageLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext.getUseCases().getCaseMessageInteractor({ + applicationContext, + messageId: event.pathParameters.messageId, + }); + }); diff --git a/web-api/src/messages/getInboxCaseMessagesForSectionLambda.js b/web-api/src/messages/getInboxCaseMessagesForSectionLambda.js new file mode 100644 index 00000000000..eedd1e8cc8d --- /dev/null +++ b/web-api/src/messages/getInboxCaseMessagesForSectionLambda.js @@ -0,0 +1,17 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * gets the inbox case messages for the section + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.getInboxCaseMessagesForSectionLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .getInboxCaseMessagesForSectionInteractor({ + applicationContext, + section: event.pathParameters.section, + }); + }); diff --git a/web-api/src/messages/getInboxCaseMessagesForUserLambda.js b/web-api/src/messages/getInboxCaseMessagesForUserLambda.js new file mode 100644 index 00000000000..9e4623c3568 --- /dev/null +++ b/web-api/src/messages/getInboxCaseMessagesForUserLambda.js @@ -0,0 +1,17 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * gets the inbox case messages for the user + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.getInboxCaseMessagesForUserLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .getInboxCaseMessagesForUserInteractor({ + applicationContext, + userId: event.pathParameters.userId, + }); + }); diff --git a/web-api/src/messages/getOutboxCaseMessagesForSectionLambda.js b/web-api/src/messages/getOutboxCaseMessagesForSectionLambda.js new file mode 100644 index 00000000000..d1ecfd66aef --- /dev/null +++ b/web-api/src/messages/getOutboxCaseMessagesForSectionLambda.js @@ -0,0 +1,17 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * gets the outbox case messages for the section + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.getOutboxCaseMessagesForSectionLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .getOutboxCaseMessagesForSectionInteractor({ + applicationContext, + section: event.pathParameters.section, + }); + }); diff --git a/web-api/src/messages/getOutboxCaseMessagesForUserLambda.js b/web-api/src/messages/getOutboxCaseMessagesForUserLambda.js new file mode 100644 index 00000000000..cc9d5493829 --- /dev/null +++ b/web-api/src/messages/getOutboxCaseMessagesForUserLambda.js @@ -0,0 +1,17 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * gets the outbox case messages for the user + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.getOutboxCaseMessagesForUserLambda = event => + genericHandler(event, async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .getOutboxCaseMessagesForUserInteractor({ + applicationContext, + userId: event.pathParameters.userId, + }); + }); diff --git a/web-api/src/messagesHandlers.js b/web-api/src/messagesHandlers.js new file mode 100644 index 00000000000..1925d327f21 --- /dev/null +++ b/web-api/src/messagesHandlers.js @@ -0,0 +1,14 @@ +module.exports = { + createCaseMessageLambda: require('./messages/createCaseMessageLambda') + .createCaseMessageLambda, + getCaseMessageLambda: require('./messages/getCaseMessageLambda') + .getCaseMessageLambda, + getInboxCaseMessagesForSectionLambda: require('./messages/getInboxCaseMessagesForSectionLambda') + .getInboxCaseMessagesForSectionLambda, + getInboxCaseMessagesForUserLambda: require('./messages/getInboxCaseMessagesForUserLambda') + .getInboxCaseMessagesForUserLambda, + getOutboxCaseMessagesForSectionLambda: require('./messages/getOutboxCaseMessagesForSectionLambda') + .getOutboxCaseMessagesForSectionLambda, + getOutboxCaseMessagesForUserLambda: require('./messages/getOutboxCaseMessagesForUserLambda') + .getOutboxCaseMessagesForUserLambda, +}; diff --git a/web-api/src/cases/getConsolidatedCasesByUserLambda.js b/web-api/src/public-api/getCaseForPublicDocketSearchLambda.js similarity index 56% rename from web-api/src/cases/getConsolidatedCasesByUserLambda.js rename to web-api/src/public-api/getCaseForPublicDocketSearchLambda.js index 907435f4cce..2298fcd164e 100644 --- a/web-api/src/cases/getConsolidatedCasesByUserLambda.js +++ b/web-api/src/public-api/getCaseForPublicDocketSearchLambda.js @@ -1,23 +1,24 @@ const { genericHandler } = require('../genericHandler'); /** - * used for fetching all cases (including consolidated) of a particular status, user role, etc + * used for fetching a single case * * @param {object} event the AWS event object * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers */ -exports.getConsolidatedCasesByUserLambda = event => +exports.getCaseForPublicDocketSearch = event => genericHandler( event, async ({ applicationContext }) => { - const { userId } = event.pathParameters || {}; - return await applicationContext .getUseCases() - .getConsolidatedCasesByUserInteractor({ + .getCaseForPublicDocketSearchInteractor({ applicationContext, - userId, + docketNumber: event.pathParameters.docketNumber, }); }, - { logResults: false, skipFiltering: true }, + { + isPublicUser: true, + user: {}, + }, ); diff --git a/web-api/src/public-api/todaysOpinionsLambda.js b/web-api/src/public-api/todaysOpinionsLambda.js new file mode 100644 index 00000000000..895cc519bb2 --- /dev/null +++ b/web-api/src/public-api/todaysOpinionsLambda.js @@ -0,0 +1,23 @@ +const { genericHandler } = require('../genericHandler'); + +/** + * used for fetching opinions created for the current date + * + * @param {object} event the AWS event object + * @returns {Promise<*|undefined>} the api gateway response object containing the statusCode, body, and headers + */ +exports.todaysOpinionsLambda = event => + genericHandler( + event, + async ({ applicationContext }) => { + return await applicationContext + .getUseCases() + .getTodaysOpinionsInteractor({ + applicationContext, + }); + }, + { + isPublicUser: true, + user: {}, + }, + ); diff --git a/web-api/src/publicApiHandlers.js b/web-api/src/publicApiHandlers.js index b4ec0b7ecc2..e0e5a01e887 100644 --- a/web-api/src/publicApiHandlers.js +++ b/web-api/src/publicApiHandlers.js @@ -3,6 +3,8 @@ module.exports = { .casePublicSearchLambda, generatePublicDocketRecordPdfLambda: require('./public-api/generatePublicDocketRecordPdfLambda') .generatePublicDocketRecordPdfLambda, + getCaseForPublicDocketSearch: require('./public-api/getCaseForPublicDocketSearchLambda') + .getCaseForPublicDocketSearch, getPublicCaseLambda: require('./public-api/getPublicCaseLambda') .getPublicCaseLambda, getPublicDocumentDownloadUrlLambda: require('./public-api/getPublicDocumentDownloadUrlLambda') @@ -13,4 +15,6 @@ module.exports = { .opinionPublicSearchLambda, orderPublicSearchLambda: require('./public-api/orderPublicSearchLambda') .orderPublicSearchLambda, + todaysOpinionsLambda: require('./public-api/todaysOpinionsLambda') + .todaysOpinionsLambda, }; diff --git a/web-api/src/streamsHandlers.js b/web-api/src/streamsHandlers.js index a25f36fbeca..6d71546f2c6 100644 --- a/web-api/src/streamsHandlers.js +++ b/web-api/src/streamsHandlers.js @@ -2,5 +2,5 @@ module.exports = { processStreamRecordsLambda: require('./streams/processStreamRecordsLambda') .processStreamRecordsLambda, reprocessFailedRecordsLambda: require('./streams/reprocessFailedRecordsLambda') - .handler, + .reprocessFailedRecordsLambda, }; diff --git a/web-api/src/usersHandlers.js b/web-api/src/usersHandlers.js index b09294bb4f3..791e0cf4708 100644 --- a/web-api/src/usersHandlers.js +++ b/web-api/src/usersHandlers.js @@ -2,8 +2,6 @@ module.exports = { createUserLambda: require('./users/createUserLambda').createUserLambda, getCasesByUserLambda: require('./cases/getCasesByUserLambda') .getCasesByUserLambda, - getConsolidatedCasesByUserLambda: require('./cases/getConsolidatedCasesByUserLambda') - .getConsolidatedCasesByUserLambda, getDocumentQCInboxForUserLambda: require('./workitems/getDocumentQCInboxForUserLambda') .getDocumentQCInboxForUserLambda, getDocumentQCServedForUserLambda: require('./workitems/getDocumentQCServedForUserLambda') diff --git a/web-api/storage/fixtures/create-random-cases.js b/web-api/storage/fixtures/create-random-cases.js index 21e51d93133..8475a00b48b 100755 --- a/web-api/storage/fixtures/create-random-cases.js +++ b/web-api/storage/fixtures/create-random-cases.js @@ -2,12 +2,11 @@ const axios = require('axios'); const faker = require('faker'); const jwt = require('jsonwebtoken'); const { - ContactFactory, -} = require('../../../shared/src/business/entities/contacts/ContactFactory'); -const { - TrialSession, -} = require('../../../shared/src/business/entities/trialSessions/TrialSession'); -const { Case } = require('../../../shared/src/business/entities/cases/Case'); + CASE_TYPES, + PARTY_TYPES, + PROCEDURE_TYPES, + TRIAL_CITIES, +} = require('../../../shared/src/business/entities/EntityConstants'); const { userMap } = require('../../../shared/src/test/mockUserTokenMap'); const USAGE = ` @@ -42,7 +41,7 @@ const main = () => { for (let i = 0; i < numToCreate; i++) { const preferredTrialCityObject = - TrialSession.TRIAL_CITIES.ALL[faker.random.number() % 74]; + TRIAL_CITIES.ALL[faker.random.number() % 74]; const preferredTrialCity = preferredTrialCityObject.city + ', ' + preferredTrialCityObject.state; @@ -51,7 +50,7 @@ const main = () => { const randomlyGeneratedData = { petitionFileId, petitionMetadata: { - caseType: Case.CASE_TYPES[faker.random.number() % 13], + caseType: CASE_TYPES[faker.random.number() % 13], contactPrimary: { address1: faker.address.streetAddress(), city: faker.address.city(), @@ -73,10 +72,10 @@ const main = () => { countryType: 'domestic', filingType: 'Myself and my spouse', hasIrsNotice: faker.random.boolean(), - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, preferredTrialCity, privatePractitioners: [], - procedureType: Case.PROCEDURE_TYPES[faker.random.number() % 2], + procedureType: PROCEDURE_TYPES[faker.random.number() % 2], }, stinFileId, }; diff --git a/web-api/storage/fixtures/get-diffs.js b/web-api/storage/fixtures/get-diffs.js index 0b699c7a227..46b330f24da 100755 --- a/web-api/storage/fixtures/get-diffs.js +++ b/web-api/storage/fixtures/get-diffs.js @@ -26,6 +26,11 @@ process.argv.forEach((val, index) => { } }); +/** + * @arg obj1 {object} + * @arg obj2 {object} + * @returns {boolean} if objects are deep-equal + */ function deepEqual(obj1, obj2) { //Loop through properties in object 1 for (let p in obj1) { @@ -36,23 +41,23 @@ function deepEqual(obj1, obj2) { if (obj2[p] === null && obj1[p] !== null) return false; switch (typeof obj1[p]) { - //Deep compare objects - case 'object': - if (!deepEqual(obj1[p], obj2[p])) return false; - break; + //Deep compare objects + case 'object': + if (!deepEqual(obj1[p], obj2[p])) return false; + break; //Compare function code - case 'function': - if ( - typeof obj2[p] == 'undefined' || + case 'function': + if ( + typeof obj2[p] == 'undefined' || (p != 'compare' && obj1[p].toString() != obj2[p].toString()) - ) - return false; - break; + ) + return false; + break; //Compare values - default: - if (obj1[p] === '' && obj2[p] !== '') return false; - if (obj2[p] === '' && obj1[p] !== '') return false; - if (obj1[p] != obj2[p]) return false; + default: + if (obj1[p] === '' && obj2[p] !== '') return false; + if (obj2[p] === '' && obj1[p] !== '') return false; + if (obj1[p] != obj2[p]) return false; } } @@ -91,4 +96,4 @@ const main = () => { console.log(JSON.stringify(differences, null, 2)); }; -main(arguments); +main(); diff --git a/web-api/storage/fixtures/seed/101-19.json b/web-api/storage/fixtures/seed/101-19.json index 9417500e287..dc6b7d118da 100755 --- a/web-api/storage/fixtures/seed/101-19.json +++ b/web-api/storage/fixtures/seed/101-19.json @@ -3,6 +3,7 @@ "pk": "case|2fa6da8d-4328-4a20-a5d7-b76637e1dc02", "sk": "docket-record|af1aa3f3-e2e3-43e6-885d-4ce341588c76", "docketRecordId": "af1aa3f3-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 2, "filingDate": "2019-03-01T21:40:46.415Z", "description": "Petition", "filedBy": "Test Petitioner", @@ -65,7 +66,15 @@ }, { "sk": "case|2fa6da8d-4328-4a20-a5d7-b76637e1dc02", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|2fa6da8d-4328-4a20-a5d7-b76637e1dc02", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "2fa6da8d-4328-4a20-a5d7-b76637e1dc02", + "createdAt": "2019-03-01T21:40:46.415Z", + "caseCaption": "Brett Osborne, Petitioner", + "docketNumber": "101-19", + "docketNumberWithSuffix": "101-19W", + "status": "New", + "entityName": "UserCase" }, { "sk": "case|2fa6da8d-4328-4a20-a5d7-b76637e1dc02", @@ -162,6 +171,7 @@ "documentType": "Petition", "entityName": "Document", "filingDate": "2019-03-01T21:40:46.415Z", + "numberOfPages": 1, "workItems": [ { "associatedJudge": "Chief Judge", @@ -234,6 +244,7 @@ "signedAt": "2019-10-07T14:29:30.288Z", "documentId": "25100ec6-eeeb-4e88-872f-c99fad1fe6c7", "entityName": "Document", + "numberOfPages": 1, "signedByUserId": "1805d1ab-18d0-43ec-bafb-654e83405416", "pk": "case|2fa6da8d-4328-4a20-a5d7-b76637e1dc02", "documentTitle": "Order of Dismissal for Lack of Jurisdiction", @@ -244,6 +255,7 @@ "documentType": "Statement of Taxpayer Identification", "filingDate": "2019-03-05T17:34:13.491Z", "workItems": [], + "numberOfPages": 1, "pending": false, "receivedAt": "2020-03-06T20:04:07.628Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", diff --git a/web-api/storage/fixtures/seed/101-20.json b/web-api/storage/fixtures/seed/101-20.json index 346afdf81ca..742ab5844f2 100644 --- a/web-api/storage/fixtures/seed/101-20.json +++ b/web-api/storage/fixtures/seed/101-20.json @@ -65,6 +65,7 @@ "eventCode": "P", "index": 1, "documentId": "cb323daf-c541-4645-bb44-232255d03d40", + "numberOfPages": 1, "filingDate": "2020-01-02T16:05:45.979Z", "filedBy": "Petr. Bill Burr" }, @@ -95,6 +96,7 @@ "sk": "document|0048b9e5-15e8-4315-8b9c-8a2e181205e4", "partySecondary": false, "documentId": "0048b9e5-15e8-4315-8b9c-8a2e181205e4", + "numberOfPages": 1, "pk": "case|0bc59d5b-c2b9-41f7-92a7-03b8cadffcc0" }, { @@ -120,6 +122,7 @@ "pending": false, "partySecondary": false, "documentId": "cb323daf-c541-4645-bb44-232255d03d40", + "numberOfPages": 1, "receivedAt": "2020-01-02T16:05:45.979Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" }, @@ -129,7 +132,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-02T16:05:45.979Z", "assigneeName": null, "caseId": "0bc59d5b-c2b9-41f7-92a7-03b8cadffcc0", @@ -156,6 +160,7 @@ "caseId": "0bc59d5b-c2b9-41f7-92a7-03b8cadffcc0", "sk": "document|cb323daf-c541-4645-bb44-232255d03d40", "partySecondary": false, + "numberOfPages": 1, "documentId": "cb323daf-c541-4645-bb44-232255d03d40", "pk": "case|0bc59d5b-c2b9-41f7-92a7-03b8cadffcc0" } diff --git a/web-api/storage/fixtures/seed/102-19.json b/web-api/storage/fixtures/seed/102-19.json index 5f247d910e9..1e3780dfd83 100755 --- a/web-api/storage/fixtures/seed/102-19.json +++ b/web-api/storage/fixtures/seed/102-19.json @@ -66,6 +66,7 @@ "filedBy": "Test Petitioner", "index": 1, "docketRecordId": "8ac50e8e-3a86-4340-8383-ae76eaa02f8b", + "numberOfPages": 2, "pk": "case|fa1179bd-04f5-4934-a716-964d8d7babc6", "sk": "docket-record|8ac50e8e-3a86-4340-8383-ae76eaa02f8b" }, @@ -84,7 +85,15 @@ }, { "sk": "case|fa1179bd-04f5-4934-a716-964d8d7babc6", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|fa1179bd-04f5-4934-a716-964d8d7babc6", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "fa1179bd-04f5-4934-a716-964d8d7babc6", + "docketNumberWithSuffix": "102-19P", + "docketNumber": "102-19", + "status": "New", + "createdAt": "2019-03-01T21:40:46.415Z", + "caseCaption": "Selma Horn & Cairo Harris, Petitioners", + "entityName": "UserCase" }, { "docketNumberWithSuffix": "102-19P", @@ -94,6 +103,7 @@ "createdAt": "2019-03-01T21:42:29.073Z", "filingDate": "2019-03-01T21:42:29.073Z", "documentId": "89c781f6-71ba-4ead-93d8-c681c2183a73", + "numberOfPages": 1, "documentType": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", "filedBy": "Test Petitioner", @@ -134,6 +144,7 @@ "filingDate": "2019-03-01T21:40:46.415Z", "createdAt": "2019-03-01T21:42:29.073Z", "documentId": "89c781f6-71ba-4ead-93d8-c681c2183a73", + "numberOfPages": 1, "documentType": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", "filedBy": "Test Petitioner", @@ -173,6 +184,7 @@ "createdAt": "2019-03-01T21:42:29.073Z", "filingDate": "2019-03-01T21:42:29.073Z", "documentId": "89c781f6-71ba-4ead-93d8-c681c2183a73", + "numberOfPages": 1, "documentType": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", "filedBy": "Test Petitioner", @@ -219,6 +231,7 @@ "createdAt": "2019-03-01T21:42:29.073Z", "documentId": "89c781f6-71ba-4ead-93d8-c681c2183a73", "documentTitle": "Petition", + "numberOfPages": 1, "documentType": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", "filedBy": "Test Petitioner" @@ -254,6 +267,7 @@ "filedBy": "Test Petitioner", "caseId": "fa1179bd-04f5-4934-a716-964d8d7babc6", "sk": "document|89c781f6-71ba-4ead-93d8-c681c2183a73", + "numberOfPages": 1, "documentId": "89c781f6-71ba-4ead-93d8-c681c2183a73", "pk": "case|fa1179bd-04f5-4934-a716-964d8d7babc6" }, @@ -267,6 +281,7 @@ "createdAt": "2019-03-05T17:34:13.491Z", "processingStatus": "complete", "filedBy": "Test Petitioner", + "numberOfPages": 1, "caseId": "fa1179bd-04f5-4934-a716-964d8d7babc6", "sk": "document|b1aa4aa2-c214-424c-8870-d0049c5744d8", "documentId": "b1aa4aa2-c214-424c-8870-d0049c5744d8", diff --git a/web-api/storage/fixtures/seed/102-20.json b/web-api/storage/fixtures/seed/102-20.json index 570244a945d..f11c13f29d0 100644 --- a/web-api/storage/fixtures/seed/102-20.json +++ b/web-api/storage/fixtures/seed/102-20.json @@ -15,6 +15,7 @@ "pending": false, "partySecondary": false, "documentId": "d854a954-7332-4e92-93bd-dc28c9fea0a5", + "numberOfPages": 1, "receivedAt": "2020-01-21T16:41:39.481Z", "filingDate": "2020-01-21T16:41:39.481Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -25,7 +26,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-21T16:41:39.482Z", "assigneeName": null, "caseId": "c862926c-119f-49d5-8cc3-6e93ab703fa3", @@ -45,7 +47,15 @@ }, { "sk": "case|c862926c-119f-49d5-8cc3-6e93ab703fa3", - "pk": "user|ad07b846-8933-4778-9fe2-b5d8ac8ad728" + "gsi1pk": "user-case|c862926c-119f-49d5-8cc3-6e93ab703fa3", + "pk": "user|ad07b846-8933-4778-9fe2-b5d8ac8ad728", + "caseId": "c862926c-119f-49d5-8cc3-6e93ab703fa3", + "createdAt": "2020-01-21T16:41:39.474Z", + "caseCaption": "Eve Brewer, Petitioner", + "docketNumber": "102-20", + "docketNumberWithSuffix": "102-20S", + "status": "New", + "entityName": "UserCase" }, { "sk": "case|c862926c-119f-49d5-8cc3-6e93ab703fa3", @@ -58,7 +68,15 @@ }, { "sk": "case|c862926c-119f-49d5-8cc3-6e93ab703fa3", - "pk": "user|5805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|c862926c-119f-49d5-8cc3-6e93ab703fa3", + "pk": "user|5805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "c862926c-119f-49d5-8cc3-6e93ab703fa3", + "createdAt": "2020-01-21T16:41:39.474Z", + "caseCaption": "Eve Brewer, Petitioner", + "docketNumber": "102-20", + "docketNumberWithSuffix": "102-20S", + "status": "New", + "entityName": "UserCase" }, { "docketNumberWithSuffix": "102-20S", @@ -76,6 +94,7 @@ "pending": false, "partySecondary": false, "documentId": "d854a954-7332-4e92-93bd-dc28c9fea0a5", + "numberOfPages": 1, "receivedAt": "2020-01-21T16:41:39.481Z", "filingDate": "2020-01-21T16:41:39.481Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -86,7 +105,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-21T16:41:39.482Z", "assigneeName": null, "caseId": "c862926c-119f-49d5-8cc3-6e93ab703fa3", @@ -158,6 +178,7 @@ "description": "Petition", "index": 1, "documentId": "d854a954-7332-4e92-93bd-dc28c9fea0a5", + "numberOfPages": 1, "filingDate": "2020-01-21T16:41:39.481Z", "filedBy": "Petr. Eve Brewer", "docketRecordId": "48f4a2be-6395-4fc0-9439-f99e365c38ff", @@ -174,12 +195,20 @@ "sk": "docket-record|f15a6532-9805-479b-ae45-e8f112153d2a" }, { - "sk": "case|77d8449a-ffe1-48e5-b056-a6112a819a4b", - "pk": "work-item|c862926c-119f-49d5-8cc3-6e93ab703fa3" + "sk": "work-item|77d8449a-ffe1-48e5-b056-a6112a819a4b", + "pk": "case|c862926c-119f-49d5-8cc3-6e93ab703fa3" }, { - "sk": "work-item|c862926c-119f-49d5-8cc3-6e93ab703fa3", - "pk": "case|7805d1ab-18d0-43ec-bafb-654e83405416" + "sk": "case|c862926c-119f-49d5-8cc3-6e93ab703fa3", + "gsi1pk": "user-case|c862926c-119f-49d5-8cc3-6e93ab703fa3", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "c862926c-119f-49d5-8cc3-6e93ab703fa3", + "createdAt": "2020-01-21T16:41:39.474Z", + "caseCaption": "Eve Brewer, Petitioner", + "docketNumber": "102-20", + "docketNumberWithSuffix": "102-20S", + "status": "New", + "entityName": "UserCase" }, { "role": "irsPractitioner", @@ -242,6 +271,7 @@ "sk": "document|3afdcbdd-2ee9-41dd-83ff-8b0ffb7d7d6a", "partySecondary": false, "documentId": "3afdcbdd-2ee9-41dd-83ff-8b0ffb7d7d6a", + "numberOfPages": 1, "pk": "case|c862926c-119f-49d5-8cc3-6e93ab703fa3" }, { @@ -267,6 +297,7 @@ "pending": false, "partySecondary": false, "documentId": "d854a954-7332-4e92-93bd-dc28c9fea0a5", + "numberOfPages": 1, "receivedAt": "2020-01-21T16:41:39.481Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" }, @@ -276,7 +307,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-21T16:41:39.482Z", "assigneeName": null, "caseId": "c862926c-119f-49d5-8cc3-6e93ab703fa3", @@ -304,6 +336,7 @@ "sk": "document|d854a954-7332-4e92-93bd-dc28c9fea0a5", "partySecondary": false, "documentId": "d854a954-7332-4e92-93bd-dc28c9fea0a5", + "numberOfPages": 1, "pk": "case|c862926c-119f-49d5-8cc3-6e93ab703fa3" } ] diff --git a/web-api/storage/fixtures/seed/103-19.json b/web-api/storage/fixtures/seed/103-19.json index 75687a33589..9aa29b8cfce 100755 --- a/web-api/storage/fixtures/seed/103-19.json +++ b/web-api/storage/fixtures/seed/103-19.json @@ -47,6 +47,7 @@ "index": 1, "filingDate": "2019-03-01T22:53:50.098Z", "filedBy": "Test Petitioner", + "numberOfPages": 2, "docketRecordId": "9aff1148-8a28-4a9d-82ea-100d3cde192d", "pk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", "sk": "docket-record|9aff1148-8a28-4a9d-82ea-100d3cde192d" @@ -66,6 +67,7 @@ "index": 3, "filingDate": "2019-03-01T22:54:05.993Z", "filedBy": "Test Respondent", + "numberOfPages": 2, "docketRecordId": "261725bd-4976-4e2b-9ce9-53540f4eff06", "pk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", "sk": "docket-record|261725bd-4976-4e2b-9ce9-53540f4eff06" @@ -75,6 +77,7 @@ "eventCode": "SIAB", "index": 4, "documentId": "dc2664a1-f552-418f-bcc7-8a67f4246568", + "numberOfPages": 1, "editState": "{\"lodged\":false,\"practitioner\":[],\"dateReceivedMonth\":\"10\",\"dateReceivedDay\":\"10\",\"dateReceivedYear\":\"2001\",\"eventCode\":\"ATOB\",\"category\":\"Simultaneous Brief\",\"documentTitle\":\"Simultaneous Answering Brief\",\"documentType\":\"Simultaneous Answering Brief\",\"scenario\":\"Standard\",\"additionalInfo\":\"abc\",\"additionalInfo2\":\"123\",\"partyPrimary\":true,\"partyIrsPractitioner\":true,\"attachments\":true,\"searchError\":false,\"serviceDate\":null,\"certificateOfServiceDate\":null,\"dateReceived\":\"2001-10-10\",\"isFileAttached\":false,\"isPaper\":true,\"docketNumber\":\"103-19\",\"caseId\":\"491b05b4-483f-4b85-8dd7-2dd4c069eb50\",\"createdAt\":\"2019-08-14T20:35:52.858Z\",\"receivedAt\":\"2001-10-10\"}", "filingDate": "2001-10-10T23:54:05.993Z", "docketRecordId": "63b1503d-26cc-448c-a4a4-670b846518c4", @@ -83,7 +86,15 @@ }, { "sk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "491b05b4-483f-4b85-8dd7-2dd4c069eb50", + "createdAt": "2019-03-01T22:53:50.097Z", + "caseCaption": "Samson Workman, Petitioner", + "docketNumber": "103-19", + "docketNumberWithSuffix": "103-19S", + "status": "New", + "entityName": "UserCase" }, { "sk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", @@ -91,7 +102,15 @@ }, { "sk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", - "pk": "user|5805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", + "pk": "user|5805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "491b05b4-483f-4b85-8dd7-2dd4c069eb50", + "createdAt": "2019-03-01T22:53:50.097Z", + "caseCaption": "Samson Workman, Petitioner", + "docketNumber": "103-19", + "docketNumberWithSuffix": "103-19S", + "status": "New", + "entityName": "UserCase" }, { "sk": "work-item|9055257a-0a95-4b80-a728-5bb754c60e59", @@ -105,6 +124,7 @@ "filingDate": "2019-03-01T21:40:46.415Z", "createdAt": "2019-03-01T22:53:50.098Z", "documentId": "c611ee2e-a270-4dcd-a7bd-b8b9062db630", + "numberOfPages": 1, "documentType": "Petition", "documentTitle": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", @@ -144,6 +164,7 @@ "receivedAt": "2019-03-01T21:40:46.415Z", "createdAt": "2019-03-01T22:53:50.098Z", "documentId": "c611ee2e-a270-4dcd-a7bd-b8b9062db630", + "numberOfPages": 1, "documentType": "Petition", "documentTitle": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", @@ -184,6 +205,7 @@ "receivedAt": "2019-03-01T21:40:46.415Z", "filingDate": "2019-03-01T21:40:46.415Z", "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7", + "numberOfPages": 1, "userId": "5805d1ab-18d0-43ec-bafb-654e83405416", "documentType": "Answer", "documentTitle": "Answer by Test Respondent", @@ -222,6 +244,7 @@ "filingDate": "2019-03-01T22:54:05.993Z", "createdAt": "2019-03-01T22:54:05.993Z", "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7", + "numberOfPages": 1, "userId": "5805d1ab-18d0-43ec-bafb-654e83405416", "documentType": "Answer", "documentTitle": "Answer by Test Respondent", @@ -264,7 +287,8 @@ "filingDate": "2019-03-01T22:54:05.993Z", "documentType": "Answer", "documentTitle": "Answer by Test Respondent", - "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7" + "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7", + "numberOfPages": 1 }, "section": "armensChambers", "workItemId": "8686ddef-c74d-4d26-8748-6b22914c7687", @@ -304,6 +328,7 @@ "filingDate": "2019-03-01T22:54:05.993Z", "documentType": "Answer", "documentTitle": "Answer by Test Respondent", + "numberOfPages": 1, "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7" }, "section": "armensChambers", @@ -345,6 +370,7 @@ "filingDate": "2019-03-01T22:54:05.993Z", "documentType": "Answer", "documentTitle": "Answer by Test Respondent", + "numberOfPages": 1, "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7" }, "section": "armensChambers", @@ -384,6 +410,7 @@ "filingDate": "2019-03-01T22:54:05.993Z", "documentType": "Answer", "documentTitle": "Answer by Test Respondent", + "numberOfPages": 1, "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7" }, "section": "armensChambers", @@ -424,6 +451,7 @@ "filingDate": "2019-03-01T22:54:05.993Z", "documentType": "Answer", "documentTitle": "Answer by Test Respondent", + "numberOfPages": 1, "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7" }, "section": "armensChambers", @@ -494,6 +522,7 @@ "caseId": "491b05b4-483f-4b85-8dd7-2dd4c069eb50", "sk": "document|b1aa4aa2-c214-424c-8870-d0049c5744d8", "documentId": "b1aa4aa2-c214-424c-8870-d0049c5744d8", + "numberOfPages": 1, "pk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50" }, { @@ -509,6 +538,7 @@ "document": { "createdAt": "2019-03-01T22:53:50.098Z", "documentId": "c611ee2e-a270-4dcd-a7bd-b8b9062db630", + "numberOfPages": 1, "documentTitle": "Petition", "documentType": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", @@ -546,6 +576,7 @@ "caseId": "491b05b4-483f-4b85-8dd7-2dd4c069eb50", "sk": "document|c611ee2e-a270-4dcd-a7bd-b8b9062db630", "documentId": "c611ee2e-a270-4dcd-a7bd-b8b9062db630", + "numberOfPages": 1, "pk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50" }, { @@ -598,6 +629,7 @@ "lodged": false, "certificateOfServiceDate": null, "documentId": "dc2664a1-f552-418f-bcc7-8a67f4246568", + "numberOfPages": 1, "partyIrsPractitioner": true, "category": "Simultaneous Brief", "docketNumber": "103-19" @@ -634,6 +666,7 @@ "lodged": false, "certificateOfServiceDate": null, "documentId": "dc2664a1-f552-418f-bcc7-8a67f4246568", + "numberOfPages": 1, "partyIrsPractitioner": true, "pk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", "docketNumber": "103-19" @@ -652,6 +685,7 @@ "createdAt": "2019-03-01T22:54:05.993Z", "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7", "documentTitle": "Answer by Test Respondent", + "numberOfPages": 1, "documentType": "Answer", "userId": "5805d1ab-18d0-43ec-bafb-654e83405416", "filedBy": "Test Respondent" @@ -680,13 +714,24 @@ ], "pending": false, "receivedAt": "2019-08-14T20:35:52.915Z", - "userId": "respondent", + "userId": "5805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-03-01T22:54:05.993Z", "processingStatus": "complete", "filedBy": "Test Respondent", "caseId": "491b05b4-483f-4b85-8dd7-2dd4c069eb50", "sk": "document|f1aa4aa2-c214-424c-8870-d0049c5744d7", "documentId": "f1aa4aa2-c214-424c-8870-d0049c5744d7", + "numberOfPages": 1, "pk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50" + }, + { + "filingDate": "2019-08-14T20:35:52.915Z", + "filedBy": "Test Petitionsclerk", + "sk": "correspondence|f1aa4aa3-c214-424c-8870-d0049c5744d7", + "documentId": "f1aa4aa3-c214-424c-8870-d0049c5744d7", + "numberOfPages": 1, + "pk": "case|491b05b4-483f-4b85-8dd7-2dd4c069eb50", + "documentTitle": "Internal Memo", + "userId": "3805d1ab-18d0-43ec-bafb-654e83405416" } ] diff --git a/web-api/storage/fixtures/seed/103-20.json b/web-api/storage/fixtures/seed/103-20.json index 84fd3963bf0..542bb405517 100644 --- a/web-api/storage/fixtures/seed/103-20.json +++ b/web-api/storage/fixtures/seed/103-20.json @@ -23,6 +23,7 @@ "pending": false, "partySecondary": false, "documentId": "ac62f25a-49f9-46a5-aed7-d6b955a2dc34", + "numberOfPages": 1, "receivedAt": "2020-01-23T21:44:54.043Z", "filingDate": "2020-01-23T21:44:54.043Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -35,7 +36,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-23T21:44:54.043Z", "highPriority": true, "assigneeName": null, @@ -79,6 +81,7 @@ "certificateOfService": false, "certificateOfServiceDate": null, "documentId": "2cca1543-21b2-4783-a1de-eeaaf269d32c", + "numberOfPages": 1, "category": "Miscellaneous", "documentTitle": "Administrative Record", "relationship": "primaryDocument", @@ -90,7 +93,8 @@ "workItemId": "2a3955ca-baad-4ca7-be23-d862d2ef5310", "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-23T21:45:34.520Z", "highPriority": true, "assigneeName": null, @@ -142,6 +146,7 @@ "certificateOfService": false, "certificateOfServiceDate": null, "documentId": "2cca1543-21b2-4783-a1de-eeaaf269d32c", + "numberOfPages": 1, "category": "Miscellaneous", "documentTitle": "Administrative Record", "relationship": "primaryDocument", @@ -153,7 +158,8 @@ "workItemId": "2a3955ca-baad-4ca7-be23-d862d2ef5310", "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-23T21:45:34.520Z", "highPriority": true, "assigneeName": null, @@ -187,6 +193,7 @@ "pending": false, "partySecondary": false, "documentId": "ac62f25a-49f9-46a5-aed7-d6b955a2dc34", + "numberOfPages": 1, "receivedAt": "2020-01-23T21:44:54.043Z", "filingDate": "2020-01-23T21:44:54.043Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -199,7 +206,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-23T21:44:54.043Z", "highPriority": true, "assigneeName": null, @@ -220,7 +228,15 @@ }, { "sk": "case|24fcb050-9c95-4d69-a149-96acba0196b8", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|24fcb050-9c95-4d69-a149-96acba0196b8", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "24fcb050-9c95-4d69-a149-96acba0196b8", + "caseCaption": "Reuben Blair, Petitioner", + "createdAt": "2020-01-23T21:45:34.520Z", + "docketNumber": "103-20", + "docketNumberWithSuffix": "103-20L", + "status": "Calendared", + "entityName": "UserCase" }, { "associatedJudge": "Judge Armen", @@ -281,8 +297,9 @@ { "eventCode": "P", "description": "Petition", - "index": 1, "documentId": "ac62f25a-49f9-46a5-aed7-d6b955a2dc34", + "numberOfPages": 1, + "index": 1, "filingDate": "2020-01-23T21:44:54.043Z", "filedBy": "Petr. Reuben Blair", "docketRecordId": "6083daae-a943-4987-a658-1d28b2f2f029", @@ -302,6 +319,7 @@ "eventCode": "NDT", "description": "Notice of Trial", "index": 3, + "numberOfPages": 1, "documentId": "4d736f72-6312-4f7f-886e-210a0074fdc3", "filingDate": "2020-01-23T21:45:14.136Z", "filedBy": "Test Docketclerk", @@ -314,6 +332,7 @@ "description": "Administrative Record", "index": 4, "documentId": "2cca1543-21b2-4783-a1de-eeaaf269d32c", + "numberOfPages": 1, "filingDate": "2020-01-23T21:45:34.520Z", "docketRecordId": "f4e8b2a1-71d3-4225-b904-1e87dbddd854", "pk": "case|24fcb050-9c95-4d69-a149-96acba0196b8", @@ -369,6 +388,7 @@ "certificateOfService": false, "certificateOfServiceDate": null, "documentId": "2cca1543-21b2-4783-a1de-eeaaf269d32c", + "numberOfPages": 1, "category": "Miscellaneous", "documentTitle": "Administrative Record", "relationship": "primaryDocument", @@ -380,7 +400,8 @@ "workItemId": "2a3955ca-baad-4ca7-be23-d862d2ef5310", "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-23T21:45:34.520Z", "highPriority": true, "assigneeName": null, @@ -403,6 +424,7 @@ "processingStatus": "complete", "certificateOfServiceDate": null, "documentId": "2cca1543-21b2-4783-a1de-eeaaf269d32c", + "numberOfPages": 1, "servedAt": "2020-01-23T21:45:34.521Z", "pk": "case|24fcb050-9c95-4d69-a149-96acba0196b8", "docketNumber": "103-20", @@ -429,6 +451,7 @@ "caseId": "24fcb050-9c95-4d69-a149-96acba0196b8", "sk": "document|4d736f72-6312-4f7f-886e-210a0074fdc3", "documentId": "4d736f72-6312-4f7f-886e-210a0074fdc3", + "numberOfPages": 1, "servedAt": "2020-01-23T21:45:14.148Z", "pk": "case|24fcb050-9c95-4d69-a149-96acba0196b8", "documentTitle": "Notice of Trial on 11/27/2020 at Houston, Texas", @@ -457,6 +480,7 @@ "pending": false, "partySecondary": false, "documentId": "ac62f25a-49f9-46a5-aed7-d6b955a2dc34", + "numberOfPages": 1, "receivedAt": "2020-01-23T21:44:54.043Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" }, @@ -466,7 +490,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-01-23T21:44:54.043Z", "assigneeName": null, "caseId": "24fcb050-9c95-4d69-a149-96acba0196b8", @@ -494,6 +519,7 @@ "sk": "document|ac62f25a-49f9-46a5-aed7-d6b955a2dc34", "partySecondary": false, "documentId": "ac62f25a-49f9-46a5-aed7-d6b955a2dc34", + "numberOfPages": 1, "pk": "case|24fcb050-9c95-4d69-a149-96acba0196b8" }, { @@ -514,6 +540,7 @@ "sk": "document|f6f9ce81-8b65-433c-8c7b-f0ffa7a74068", "partySecondary": false, "documentId": "f6f9ce81-8b65-433c-8c7b-f0ffa7a74068", + "numberOfPages": 1, "pk": "case|24fcb050-9c95-4d69-a149-96acba0196b8" } ] diff --git a/web-api/storage/fixtures/seed/104-19.json b/web-api/storage/fixtures/seed/104-19.json index a65bb9905d3..7060664cc82 100755 --- a/web-api/storage/fixtures/seed/104-19.json +++ b/web-api/storage/fixtures/seed/104-19.json @@ -59,6 +59,7 @@ "description": "Petition", "eventCode": "P", "documentId": "c63be3f2-2240-451e-b6bd-8206d52a070b", + "numberOfPages": 1, "filingDate": "2019-03-05T17:34:13.490Z", "filedBy": "Test Petitioner", "index": 1, @@ -79,6 +80,7 @@ "description": "Ownership Disclosure Statement", "eventCode": "DISC", "documentId": "add36745-8e77-450b-a8dd-6f34272fe1d5", + "numberOfPages": 1, "filingDate": "2019-03-05T17:34:13.491Z", "filedBy": "Test Petitioner", "index": 3, @@ -88,7 +90,15 @@ }, { "sk": "case|5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "caseCaption": "Mufutau Wade, Petitioner", + "caseId": "5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", + "createdAt": "2019-03-05T17:34:13.490Z", + "docketNumber": "104-19", + "docketNumberWithSuffix": "104-19L", + "status": "New", + "entityName": "UserCase" }, { "sk": "case|5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", @@ -106,6 +116,7 @@ "receivedAt": "2019-03-01T21:40:46.415Z", "filingDate": "2019-03-01T21:40:46.415Z", "documentId": "c63be3f2-2240-451e-b6bd-8206d52a070b", + "numberOfPages": 1, "documentType": "Petition", "documentTitle": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", @@ -146,6 +157,7 @@ "receivedAt": "2019-03-01T21:40:46.415Z", "filingDate": "2019-03-01T21:40:46.415Z", "documentId": "c63be3f2-2240-451e-b6bd-8206d52a070b", + "numberOfPages": 1, "documentType": "Petition", "documentTitle": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", @@ -229,6 +241,7 @@ "caseId": "5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", "sk": "document|add36745-8e77-450b-a8dd-6f34272fe1d5", "documentId": "add36745-8e77-450b-a8dd-6f34272fe1d5", + "numberOfPages": 1, "pk": "case|5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05" }, { @@ -245,6 +258,7 @@ "caseId": "5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", "sk": "document|b1aa4aa2-c214-424c-8870-d0049c5744d8", "documentId": "b1aa4aa2-c214-424c-8870-d0049c5744d8", + "numberOfPages": 1, "pk": "case|5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05" }, { @@ -260,6 +274,7 @@ "document": { "createdAt": "2019-03-05T17:34:13.490Z", "documentId": "c63be3f2-2240-451e-b6bd-8206d52a070b", + "numberOfPages": 1, "documentTitle": "Petition", "documentType": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", @@ -297,6 +312,7 @@ "caseId": "5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", "sk": "document|c63be3f2-2240-451e-b6bd-8206d52a070b", "documentId": "c63be3f2-2240-451e-b6bd-8206d52a070b", + "numberOfPages": 1, "pk": "case|5f6e8b8e-4fac-4fd7-bf3c-42f0d7c3ca05", "documentTitle": "Petition" } diff --git a/web-api/storage/fixtures/seed/104-20.json b/web-api/storage/fixtures/seed/104-20.json index 49b73d50d56..bec659be234 100644 --- a/web-api/storage/fixtures/seed/104-20.json +++ b/web-api/storage/fixtures/seed/104-20.json @@ -26,7 +26,7 @@ } ], "attachments": false, - "documentType": "ODD - Order of Dismissal and Decision Entered,", + "documentType": "ODD - Order of Dismissal and Decision Entered", "entityName": "Document", "filingDate": "2020-04-14T03:01:15.215Z", "pending": false, @@ -54,7 +54,7 @@ "docketNumber": "104-20" }, "attachments": false, - "documentType": "ODD - Order of Dismissal and Decision Entered,", + "documentType": "ODD - Order of Dismissal and Decision Entered", "filingDate": "2020-04-14T03:01:15.215Z", "pending": false, "documentContents": "Déjà vu, this is a seed order in case 104-20 filed on Apr 13 at 11:01pm ET\n", @@ -69,6 +69,7 @@ "caseId": "1a92894e-83a5-48ba-9994-3ada44235deb", "signedAt": "2020-04-14T03:01:15.215Z", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "documentTitle": "Order of Dismissal and Decision Entered, Judge Buch", "relationship": "primaryDocument", @@ -106,6 +107,7 @@ "processingStatus": "complete", "signedAt": "2020-04-14T03:01:15.215Z", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "servedAt": "2020-04-14T03:01:50.399Z", "docketNumber": "104-20", @@ -202,7 +204,8 @@ "description": "Order of Dismissal and Decision Entered, Judge Buch", "index": 3, "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", - "editState": "{\"eventCode\":\"ODD\",\"documentType\":\"ODD - Order of Dismissal and Decision Entered,\",\"documentTitle\":\"Order of Dismissal and Decision Entered, [Judge Name] [Anything]\",\"scenario\":\"Type B\",\"attachments\":false,\"date\":null,\"generatedDocumentTitle\":\"Order of Dismissal and Decision Entered, Judge Buch\",\"judge\":\"Judge Buch\",\"caseId\":\"1a92894e-83a5-48ba-9994-3ada44235deb\",\"documentId\":\"1f1aa3f7-e2e3-43e6-885d-4ce341588c76\"}", + "numberOfPages": 1, + "editState": "{\"eventCode\":\"ODD\",\"documentType\":\"ODD - Order of Dismissal and Decision Entered,\",\"documentTitle\":\"Order of Dismissal and Decision Entered, [Judge Name] [Anything]\",\"scenario\":\"Type B\",\"attachments\":false,\"date\":null,\"numberOfPages\":\"1\",\"generatedDocumentTitle\":\"Order of Dismissal and Decision Entered, Judge Buch\",\"judge\":\"Judge Buch\",\"caseId\":\"1a92894e-83a5-48ba-9994-3ada44235deb\",\"documentId\":\"1f1aa3f7-e2e3-43e6-885d-4ce341588c76\"}", "pk": "case|1a92894e-83a5-48ba-9994-3ada44235deb" }, { @@ -225,6 +228,7 @@ "description": "Petition", "index": 1, "documentId": "596223c1-527b-46b4-98b0-1b10455e9495", + "numberOfPages": 1, "pk": "case|1a92894e-83a5-48ba-9994-3ada44235deb" }, { @@ -241,7 +245,7 @@ } ], "attachments": false, - "documentType": "ODD - Order of Dismissal and Decision Entered,", + "documentType": "ODD - Order of Dismissal and Decision Entered", "entityName": "Document", "filingDate": "2020-04-14T03:01:15.215Z", "pending": false, @@ -274,7 +278,7 @@ } ], "attachments": false, - "documentType": "ODD - Order of Dismissal and Decision Entered,", + "documentType": "ODD - Order of Dismissal and Decision Entered", "filingDate": "2020-04-14T03:01:15.215Z", "pending": false, "receivedAt": "2020-04-14T03:01:15.215Z", @@ -301,7 +305,7 @@ "docketNumber": "104-20" }, "attachments": false, - "documentType": "ODD - Order of Dismissal and Decision Entered,", + "documentType": "ODD - Order of Dismissal and Decision Entered", "filingDate": "2020-04-14T03:01:15.215Z", "pending": false, "documentContents": "Déjà vu, this is a seed order in case 104-20 filed on Apr 13 at 11:01pm ET\n", @@ -316,6 +320,7 @@ "caseId": "1a92894e-83a5-48ba-9994-3ada44235deb", "signedAt": "2020-04-14T03:01:15.215Z", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "documentTitle": "Order of Dismissal and Decision Entered, Judge Buch", "relationship": "primaryDocument", @@ -353,6 +358,7 @@ "processingStatus": "complete", "signedAt": "2020-04-14T03:01:15.215Z", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "servedAt": "2020-04-14T03:01:50.399Z", "docketNumber": "104-20", @@ -392,6 +398,7 @@ "processingStatus": "complete", "signedAt": "2020-04-14T03:01:15.215Z", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "pk": "case|1a92894e-83a5-48ba-9994-3ada44235deb", "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "servedAt": "2020-04-14T03:01:50.399Z", @@ -429,6 +436,7 @@ "sk": "document|562b0a52-7960-4674-acb7-22b65cd1cf2b", "partySecondary": false, "documentId": "562b0a52-7960-4674-acb7-22b65cd1cf2b", + "numberOfPages": 1, "pk": "case|1a92894e-83a5-48ba-9994-3ada44235deb", "servedAt": "2020-04-14T19:59:46.673Z", "status": "served" @@ -470,7 +478,8 @@ "createdAt": "2020-04-12T04:00:00.000Z", "filedBy": "Petr. Hanan Al Hroub", "partySecondary": false, - "documentId": "596223c1-527b-46b4-98b0-1b10455e9495" + "documentId": "596223c1-527b-46b4-98b0-1b10455e9495", + "numberOfPages": 1 }, "section": "petitions", "workItemId": "2020d554-30c7-450a-ac01-738c1996c783", @@ -514,6 +523,7 @@ "sk": "document|596223c1-527b-46b4-98b0-1b10455e9495", "partySecondary": false, "documentId": "596223c1-527b-46b4-98b0-1b10455e9495", + "numberOfPages": 1, "pk": "case|1a92894e-83a5-48ba-9994-3ada44235deb", "servedAt": "2020-04-14T19:59:46.671Z", "status": "served" @@ -575,7 +585,8 @@ "createdAt": "2020-04-12T04:00:00.000Z", "filedBy": "Petr. Hanan Al Hroub", "partySecondary": false, - "documentId": "596223c1-527b-46b4-98b0-1b10455e9495" + "documentId": "596223c1-527b-46b4-98b0-1b10455e9495", + "numberOfPages": 1 }, "section": "petitions", "workItemId": "2020d554-30c7-450a-ac01-738c1996c783", @@ -627,7 +638,8 @@ "createdAt": "2020-04-12T04:00:00.000Z", "filedBy": "Petr. Hanan Al Hroub", "partySecondary": false, - "documentId": "596223c1-527b-46b4-98b0-1b10455e9495" + "documentId": "596223c1-527b-46b4-98b0-1b10455e9495", + "numberOfPages": 1 }, "gsi1pk": "work-item|2020d554-30c7-450a-ac01-738c1996c783", "section": "petitions", diff --git a/web-api/storage/fixtures/seed/105-19.json b/web-api/storage/fixtures/seed/105-19.json index f09a3d1aeeb..7e502d2384e 100755 --- a/web-api/storage/fixtures/seed/105-19.json +++ b/web-api/storage/fixtures/seed/105-19.json @@ -49,6 +49,7 @@ "description": "Petition", "eventCode": "P", "documentId": "35306555-83f2-4f61-9be6-438bd107e5eb", + "numberOfPages": 1, "filingDate": "2019-03-27T21:53:00.297Z", "filedBy": "Test Practitioner", "index": 1, @@ -67,7 +68,15 @@ }, { "sk": "case|6f3d97f8-1bdd-4779-a150-c076d08ad8fd", - "pk": "user|9805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|6f3d97f8-1bdd-4779-a150-c076d08ad8fd", + "pk": "user|9805d1ab-18d0-43ec-bafb-654e83405416", + "caseCaption": "Caryn Wall, Petitioner", + "caseId": "6f3d97f8-1bdd-4779-a150-c076d08ad8fd", + "createdAt": "2019-03-27T21:53:00.297Z", + "docketNumber": "105-19", + "docketNumberWithSuffix": "105-19", + "status": "New", + "entityName": "UserCase" }, { "sk": "case|6f3d97f8-1bdd-4779-a150-c076d08ad8fd", @@ -85,6 +94,7 @@ "receivedAt": "2019-03-01T21:40:46.415Z", "filingDate": "2019-03-01T21:40:46.415Z", "documentId": "35306555-83f2-4f61-9be6-438bd107e5eb", + "numberOfPages": 1, "documentType": "Petition", "documentTitle": "Petition", "userId": "9805d1ab-18d0-43ec-bafb-654e83405416", @@ -96,7 +106,8 @@ "isQC": true, "isInitializeCase": true, "assigneeId": null, - "sentBy": "9805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Private Practitioner", + "sentByUserId": "9805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-03-27T21:53:00.297Z", "assigneeName": null, "caseId": "6f3d97f8-1bdd-4779-a150-c076d08ad8fd", @@ -124,6 +135,7 @@ "receivedAt": "2019-03-01T21:40:46.415Z", "filingDate": "2019-03-01T21:40:46.415Z", "documentId": "35306555-83f2-4f61-9be6-438bd107e5eb", + "numberOfPages": 1, "documentType": "Petition", "documentTitle": "Petition", "userId": "9805d1ab-18d0-43ec-bafb-654e83405416", @@ -135,7 +147,8 @@ "isQC": true, "isInitializeCase": true, "assigneeId": null, - "sentBy": "9805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Private Practitioner", + "sentByUserId": "9805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-03-27T21:53:00.297Z", "assigneeName": null, "caseId": "6f3d97f8-1bdd-4779-a150-c076d08ad8fd", @@ -191,6 +204,7 @@ "document": { "createdAt": "2019-03-27T21:53:00.297Z", "documentId": "35306555-83f2-4f61-9be6-438bd107e5eb", + "numberOfPages": 1, "documentTitle": "Petition", "documentType": "Petition", "userId": "9805d1ab-18d0-43ec-bafb-654e83405416", @@ -201,7 +215,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "9805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Private Practitioner", + "sentByUserId": "9805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-03-27T21:53:00.297Z", "assigneeName": null, "caseId": "6f3d97f8-1bdd-4779-a150-c076d08ad8fd", @@ -227,6 +242,7 @@ "caseId": "6f3d97f8-1bdd-4779-a150-c076d08ad8fd", "sk": "document|35306555-83f2-4f61-9be6-438bd107e5eb", "documentId": "35306555-83f2-4f61-9be6-438bd107e5eb", + "numberOfPages": 1, "pk": "case|6f3d97f8-1bdd-4779-a150-c076d08ad8fd" }, { @@ -243,6 +259,7 @@ "caseId": "6f3d97f8-1bdd-4779-a150-c076d08ad8fd", "sk": "document|f1aa4aa3-c214-424c-8870-d0049c5744d7", "documentId": "f1aa4aa3-c214-424c-8870-d0049c5744d7", + "numberOfPages": 1, "pk": "case|6f3d97f8-1bdd-4779-a150-c076d08ad8fd" } ] diff --git a/web-api/storage/fixtures/seed/105-20.json b/web-api/storage/fixtures/seed/105-20.json index 6f3d079efce..84969f6031b 100644 --- a/web-api/storage/fixtures/seed/105-20.json +++ b/web-api/storage/fixtures/seed/105-20.json @@ -58,6 +58,7 @@ "freeText": "Order that this case is sealed", "signedAt": "2020-04-29T15:52:05.726Z", "documentId": "06f60736-5f37-4590-b62a-5c7edf84ffc6", + "numberOfPages": 1, "signedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", "servedAt": "2020-04-29T15:52:16.484Z", "docketNumber": "105-20", @@ -99,6 +100,7 @@ "freeText": "Order that this case is sealed", "signedAt": "2020-04-29T15:52:05.726Z", "documentId": "06f60736-5f37-4590-b62a-5c7edf84ffc6", + "numberOfPages": 1, "pk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6", "signedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", "servedAt": "2020-04-29T15:52:16.484Z", @@ -115,6 +117,7 @@ "description": "Order that this case is sealed", "index": 3, "documentId": "06f60736-5f37-4590-b62a-5c7edf84ffc6", + "numberOfPages": 1, "editState": "{\"eventCode\":\"O\",\"documentType\":\"O - Order\",\"documentContents\":\"We are sealing this case right meow\n\",\"documentTitle\":\"[Anything]\",\"scenario\":\"Type A\",\"attachments\":false,\"freeText\":\"Order that this case is sealed\",\"serviceStamp\":\"Served\",\"generatedDocumentTitle\":\"Order that this case is sealed\",\"caseId\":\"28caad58-df69-4a4d-af15-6554aee7fbd6\",\"documentId\":\"06f60736-5f37-4590-b62a-5c7edf84ffc6\"}", "pk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6" }, @@ -209,6 +212,7 @@ "freeText": "Some very strong opinions about sunglasses", "signedAt": "2020-05-13T14:50:04.930Z", "documentId": "130a3790-7e82-4f5c-8158-17f5d9d560e7", + "numberOfPages": 1, "judge": "Judge Armen", "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "documentTitle": "T.C. Opinion Judge Armen Some very strong opinions about sunglasses", @@ -325,6 +329,7 @@ "filedBy": "Petr. Astra Santiago", "partySecondary": false, "documentId": "af9e2d43-1255-4e3d-80d0-63f0aedfab5a", + "numberOfPages": 1, "privatePractitioners": [] }, "caseTitle": "Astra Santiago", @@ -334,7 +339,8 @@ "assigneeId": null, "isQC": true, "completedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-04-29T15:50:41.698Z", "assigneeName": null, "entityName": "WorkItem", @@ -367,6 +373,7 @@ "sk": "document|af9e2d43-1255-4e3d-80d0-63f0aedfab5a", "partySecondary": false, "documentId": "af9e2d43-1255-4e3d-80d0-63f0aedfab5a", + "numberOfPages": 1, "pk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6", "servedAt": "2020-04-29T15:51:29.168Z", "privatePractitioners": [] @@ -388,13 +395,15 @@ "filedBy": "Petr. Astra Santiago", "partySecondary": false, "documentId": "af9e2d43-1255-4e3d-80d0-63f0aedfab5a", + "numberOfPages": 1, "privatePractitioners": [] }, "gsi1pk": "work-item|332026fb-8844-49b6-bb13-66eff6882f00", "section": "petitions", "workItemId": "332026fb-8844-49b6-bb13-66eff6882f00", "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-04-29T15:50:41.698Z", "assigneeName": null, "entityName": "WorkItem", @@ -482,6 +491,7 @@ "freeText": "Order that this case is sealed", "signedAt": "2020-04-29T15:52:05.726Z", "documentId": "06f60736-5f37-4590-b62a-5c7edf84ffc6", + "numberOfPages": 1, "signedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", "documentTitle": "Order that this case is sealed", "relationship": "primaryDocument", @@ -522,6 +532,7 @@ "freeText": "Order that this case is sealed", "signedAt": "2020-04-29T15:52:05.726Z", "documentId": "06f60736-5f37-4590-b62a-5c7edf84ffc6", + "numberOfPages": 1, "signedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", "servedAt": "2020-04-29T15:52:16.484Z", "docketNumber": "105-20", @@ -562,7 +573,15 @@ }, { "sk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|28caad58-df69-4a4d-af15-6554aee7fbd6", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "28caad58-df69-4a4d-af15-6554aee7fbd6", + "caseCaption": "Astra Santiago, Petitioner", + "createdAt": "2020-04-29T15:50:41.686Z", + "docketNumber": "105-20", + "docketNumberWithSuffix": "105-20L", + "status": "General Docket - Not at Issue", + "entityName": "UserCase" }, { "sk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6", @@ -570,7 +589,15 @@ }, { "sk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6", - "pk": "user|9805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|28caad58-df69-4a4d-af15-6554aee7fbd6", + "pk": "user|9805d1ab-18d0-43ec-bafb-654e83405416", + "caseId": "28caad58-df69-4a4d-af15-6554aee7fbd6", + "caseCaption": "Astra Santiago, Petitioner", + "createdAt": "2020-04-29T15:50:41.686Z", + "docketNumber": "105-20", + "docketNumberWithSuffix": "105-20L", + "status": "General Docket - Not at Issue", + "entityName": "UserCase" }, { "associatedJudge": "Chief Judge", @@ -586,7 +613,7 @@ "orderForFilingFee": false, "partyType": "Petitioner", "receivedAt": "2020-04-29T15:51:28.572Z", - "caseType": "CDP (Lien/Levy)", + "caseType": "Deficiency", "orderDesignatingPlaceOfTrial": false, "createdAt": "2020-04-29T15:50:41.686Z", "contactPrimary": { @@ -619,13 +646,32 @@ "preferredTrialCity": "New York City, New York", "initialCaption": "Astra Santiago, Petitioner", "hasPendingItems": false, + "hasVerifiedIrsNotice": true, "petitionPaymentDate": null, "orderForAmendedPetitionAndFilingFee": false, "pk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6", "docketNumber": "105-20", "docketNumberWithSuffix": "105-20L", "petitionPaymentWaivedDate": null, - "status": "General Docket - Not at Issue" + "status": "General Docket - Not at Issue", + "statistics": [ + { + "yearOrPeriod": "Year", + "year": "2018", + "irsTotalPenalties": 1234, + "irsDeficiencyAmount": 5678, + "statisticId": "cb557361-50ee-4440-aaff-0a9f1bfa30ed" + }, + { + "yearOrPeriod": "Year", + "year": "2019", + "irsTotalPenalties": 99, + "irsDeficiencyAmount": 55, + "statisticId": "ab557361-50ee-4440-aaff-0a9f1bfa30ed" + } + ], + "damages": 9999, + "litigationCosts": 19824.99 }, { "eventCode": "TCOP", @@ -636,6 +682,7 @@ "description": "T.C. Opinion Judge Armen Some very strong opinions about sunglasses", "index": 4, "documentId": "130a3790-7e82-4f5c-8158-17f5d9d560e7", + "numberOfPages": 1, "editState": "{\"eventCode\":\"TCOP\",\"documentType\":\"TCOP - T.C. Opinion\",\"documentTitle\":\"T.C. Opinion [judge] [anything]\",\"scenario\":\"Type B\",\"attachments\":false,\"date\":null,\"generatedDocumentTitle\":\"T.C. Opinion Judge Armen Some very strong opinions about sunglasses\",\"judge\":\"Judge Armen\",\"freeText\":\"Some very strong opinions about sunglasses\",\"caseId\":\"28caad58-df69-4a4d-af15-6554aee7fbd6\",\"documentId\":\"130a3790-7e82-4f5c-8158-17f5d9d560e7\"}", "pk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6" }, @@ -649,6 +696,7 @@ "description": "Petition", "index": 1, "documentId": "af9e2d43-1255-4e3d-80d0-63f0aedfab5a", + "numberOfPages": 1, "pk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6" }, { @@ -686,6 +734,7 @@ "sk": "document|abba69d5-a96a-423c-937f-b4d1c5442bf4", "partySecondary": false, "documentId": "abba69d5-a96a-423c-937f-b4d1c5442bf4", + "numberOfPages": 1, "pk": "case|28caad58-df69-4a4d-af15-6554aee7fbd6", "servedAt": "2020-04-29T15:51:29.168Z", "privatePractitioners": [] @@ -783,6 +832,7 @@ "freeText": "Order that this case is sealed", "signedAt": "2020-04-29T15:52:05.726Z", "documentId": "06f60736-5f37-4590-b62a-5c7edf84ffc6", + "numberOfPages": 1, "signedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", "documentTitle": "Order that this case is sealed", "relationship": "primaryDocument", @@ -823,6 +873,7 @@ "freeText": "Order that this case is sealed", "signedAt": "2020-04-29T15:52:05.726Z", "documentId": "06f60736-5f37-4590-b62a-5c7edf84ffc6", + "numberOfPages": 1, "signedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", "servedAt": "2020-04-29T15:52:16.484Z", "docketNumber": "105-20", @@ -881,6 +932,7 @@ "filedBy": "Petr. Astra Santiago", "partySecondary": false, "documentId": "af9e2d43-1255-4e3d-80d0-63f0aedfab5a", + "numberOfPages": 1, "privatePractitioners": [] }, "caseTitle": "Astra Santiago", @@ -890,7 +942,8 @@ "assigneeId": null, "isQC": true, "completedByUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-04-29T15:50:41.698Z", "assigneeName": null, "entityName": "WorkItem", @@ -974,6 +1027,7 @@ "freeText": "Some very strong opinions about sunglasses", "signedAt": "2020-05-13T14:50:04.930Z", "documentId": "130a3790-7e82-4f5c-8158-17f5d9d560e7", + "numberOfPages": 1, "judge": "Judge Armen", "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "documentTitle": "T.C. Opinion Judge Armen Some very strong opinions about sunglasses", @@ -1114,6 +1168,7 @@ "freeText": "Some very strong opinions about sunglasses", "signedAt": "2020-05-13T14:50:04.930Z", "documentId": "130a3790-7e82-4f5c-8158-17f5d9d560e7", + "numberOfPages": 1, "judge": "Judge Armen", "signedByUserId": "2805d1ab-18d0-43ec-bafb-654e83405416", "documentTitle": "T.C. Opinion Judge Armen Some very strong opinions about sunglasses", @@ -1207,6 +1262,7 @@ "entityName": "Document", "filedBy": "Petr. Astra Santiago", "partySecondary": false, + "numberOfPages": 1, "documentId": "af9e2d43-1255-4e3d-80d0-63f0aedfab5a", "privatePractitioners": [] }, @@ -1214,7 +1270,8 @@ "section": "petitions", "workItemId": "332026fb-8844-49b6-bb13-66eff6882f00", "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2020-04-29T15:50:41.698Z", "assigneeName": null, "entityName": "WorkItem", diff --git a/web-api/storage/fixtures/seed/106-19.json b/web-api/storage/fixtures/seed/106-19.json index 5776c086d6d..fe3ce4670da 100755 --- a/web-api/storage/fixtures/seed/106-19.json +++ b/web-api/storage/fixtures/seed/106-19.json @@ -10,6 +10,7 @@ "documentType": "Petition", "filedBy": "Denise Gould", "workItems": [], + "numberOfPages": 1, "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", "receivedAt": "2019-07-12T17:09:41.026Z", "filingDate": "2019-07-12T17:09:41.026Z", @@ -19,7 +20,8 @@ "workItemId": "85d71a2a-d1c8-448a-9c9f-2d5eb31784b7", "isInitializeCase": true, "assigneeId": null, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "isQC": true, "createdAt": "2019-07-12T17:09:41.027Z", "assigneeName": null, @@ -64,6 +66,7 @@ "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", "documentTitle": "Proposed Stipulated Decision", + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, "section": "adc", @@ -100,6 +103,7 @@ "gsi1pk": "work-item|5059e127-1796-45aa-96bc-351cd5705c66", "document": { "createdAt": "2019-07-12T17:11:26.955Z", + "numberOfPages": 1, "documentType": "Proposed Stipulated Decision", "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, @@ -158,6 +162,7 @@ "category": "Decision", "documentTitle": "Proposed Stipulated Decision", "relationship": "primaryDocument", + "numberOfPages": 1, "docketNumber": "106-19" }, "section": "docket", @@ -212,6 +217,7 @@ "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", "category": "Decision", + "numberOfPages": 1, "documentTitle": "Proposed Stipulated Decision", "relationship": "primaryDocument", "docketNumber": "106-19" @@ -250,7 +256,8 @@ "document": { "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", - "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" + "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", + "numberOfPages": 1 }, "section": "adc", "workItemId": "5059e127-1796-45aa-96bc-351cd5705c66", @@ -290,6 +297,7 @@ "documentType": "Petition", "filedBy": "Denise Gould", "workItems": [], + "numberOfPages": 1, "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", "receivedAt": "2019-07-12T17:09:41.026Z", "filingDate": "2019-07-12T17:09:41.026Z", @@ -299,7 +307,8 @@ "workItemId": "85d71a2a-d1c8-448a-9c9f-2d5eb31784b7", "isInitializeCase": true, "assigneeId": null, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "isQC": true, "createdAt": "2019-07-12T17:09:41.027Z", "assigneeName": null, @@ -343,6 +352,7 @@ "filedBy": "Petr. Denise Gould", "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", + "numberOfPages": 1, "category": "Decision", "documentTitle": "Proposed Stipulated Decision", "relationship": "primaryDocument", @@ -381,6 +391,7 @@ "gsi1pk": "work-item|5059e127-1796-45aa-96bc-351cd5705c66", "document": { "createdAt": "2019-07-12T17:11:26.955Z", + "numberOfPages": 1, "documentType": "Proposed Stipulated Decision", "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, @@ -419,6 +430,7 @@ "document": { "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, "section": "adc", @@ -457,6 +469,7 @@ "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", "documentTitle": "Proposed Stipulated Decision", + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, "section": "armensChambers", @@ -535,6 +548,7 @@ "description": "Petition", "eventCode": "P", "index": 1, + "numberOfPages": 1, "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", "filingDate": "2019-07-12T17:09:41.026Z", "filedBy": "Denise Gould", @@ -555,6 +569,7 @@ "description": "Proposed Stipulated Decision", "eventCode": "PSDE", "index": 3, + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", "filingDate": "1990-10-10", "docketRecordId": "a79bbbca-1afe-41a2-a2a6-f4befd22546c", @@ -567,7 +582,15 @@ }, { "sk": "case|d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-07-12T17:09:41.026Z", + "caseCaption": "Denise Gould, Petitioner", + "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", + "status": "New", + "docketNumber": "106-19", + "docketNumberWithSuffix": "106-19", + "entityName": "UserCase" }, { "docketNumberSuffix": null, @@ -578,6 +601,7 @@ "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", "documentTitle": "Proposed Stipulated Decision", + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, "section": "armensChambers", @@ -617,6 +641,7 @@ "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", "documentTitle": "Proposed Stipulated Decision", + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, "section": "armensChambers", @@ -656,7 +681,8 @@ "createdAt": "2019-07-12T17:11:26.955Z", "documentTitle": "Proposed Stipulated Decision", "documentType": "Proposed Stipulated Decision", - "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" + "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", + "numberOfPages": 1 }, "section": "armensChambers", "caseTitle": "Denise Gould", @@ -699,6 +725,7 @@ "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", "documentTitle": "Proposed Stipulated Decision", + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, "section": "armensChambers", @@ -761,6 +788,7 @@ "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", "sk": "document|2da6d239-555a-40e8-af81-1949c8270cd7", "documentId": "2da6d239-555a-40e8-af81-1949c8270cd7", + "numberOfPages": 1, "pk": "case|d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88" }, { @@ -780,6 +808,7 @@ "filingDate": "2019-07-12T17:09:41.026Z", "filedBy": "Denise Gould", "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "documentTitle": "Petition", "receivedAt": "2019-07-12T17:09:41.026Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -789,7 +818,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-07-12T17:09:41.027Z", "assigneeName": null, "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", @@ -815,6 +845,7 @@ "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", "sk": "document|5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "pk": "case|d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88" }, { @@ -850,6 +881,7 @@ "filedBy": "Petr. Denise Gould", "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", + "numberOfPages": 1, "category": "Decision", "documentTitle": "Proposed Stipulated Decision", "relationship": "primaryDocument", @@ -887,6 +919,7 @@ "document": { "createdAt": "2019-07-12T17:11:26.955Z", "documentType": "Proposed Stipulated Decision", + "numberOfPages": 1, "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f" }, "section": "adc", @@ -927,6 +960,7 @@ "caseId": "d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", "sk": "document|7a923abd-fc41-407a-b76b-7f724fa5d47f", "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", + "numberOfPages": 1, "pk": "case|d3d92ca6-d9b3-4bd6-8328-e94a9fc36f88", "documentTitle": "Proposed Stipulated Decision", "relationship": "primaryDocument", diff --git a/web-api/storage/fixtures/seed/107-19.json b/web-api/storage/fixtures/seed/107-19.json index e4ebe9794fc..765a79100a8 100644 --- a/web-api/storage/fixtures/seed/107-19.json +++ b/web-api/storage/fixtures/seed/107-19.json @@ -22,11 +22,27 @@ }, { "sk": "case|58c1f7a3-8062-42f0-a73e-8bd69b419878", - "pk": "user|5805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|58c1f7a3-8062-42f0-a73e-8bd69b419878", + "pk": "user|5805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-08-16T17:29:10.132Z", + "caseCaption": "Tatum Craig, Petitioner", + "caseId": "58c1f7a3-8062-42f0-a73e-8bd69b419878", + "docketNumber": "107-19", + "docketNumberWithSuffix": "107-19L", + "status": "General Docket - At Issue (Ready for Trial)", + "entityName": "UserCase" }, { "sk": "case|58c1f7a3-8062-42f0-a73e-8bd69b419878", - "pk": "user|9805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|58c1f7a3-8062-42f0-a73e-8bd69b419878", + "pk": "user|9805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-08-16T17:29:10.132Z", + "caseCaption": "Tatum Craig, Petitioner", + "caseId": "58c1f7a3-8062-42f0-a73e-8bd69b419878", + "docketNumber": "107-19", + "docketNumberWithSuffix": "107-19L", + "status": "General Docket - At Issue (Ready for Trial)", + "entityName": "UserCase" }, { "completedAt": "2019-08-16T17:30:10.526Z", @@ -42,6 +58,7 @@ "filedBy": "Tatum Craig, Wayne Obrien, Partnership Representative", "workItems": [], "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "receivedAt": "2019-08-16T17:29:10.132Z", "filingDate": "2019-08-16T17:29:10.132Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -88,6 +105,7 @@ "filedBy": "Tatum Craig, Wayne Obrien, Partnership Representative", "workItems": [], "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "receivedAt": "2019-08-16T17:29:10.132Z", "filingDate": "2019-08-16T17:29:10.132Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -171,6 +189,7 @@ "eventCode": "P", "index": 1, "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "filingDate": "2019-08-16T17:29:10.132Z", "filedBy": "Tatum Craig, Wayne Obrien, Partnership Representative", "status": "R served on 08/16/2019 12:30 PM", @@ -191,6 +210,7 @@ "description": "Ownership Disclosure Statement", "eventCode": "DISC", "index": 3, + "numberOfPages": 2, "documentId": "35306555-83f2-4f61-9be6-438bd107e5eb", "filingDate": "2019-08-16T17:29:10.133Z", "filedBy": "Tatum Craig, Wayne Obrien, Partnership Representative", @@ -213,6 +233,7 @@ "filedBy": "Tatum Craig, Wayne Obrien, Partnership Representative", "workItems": [], "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "receivedAt": "2019-08-16T17:29:10.132Z", "filingDate": "2019-08-16T17:29:10.132Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -246,7 +267,15 @@ }, { "sk": "case|58c1f7a3-8062-42f0-a73e-8bd69b419878", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|58c1f7a3-8062-42f0-a73e-8bd69b419878", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-08-16T17:29:10.132Z", + "caseCaption": "Tatum Craig, Petitioner", + "caseId": "58c1f7a3-8062-42f0-a73e-8bd69b419878", + "docketNumber": "107-19", + "docketNumberWithSuffix": "107-19L", + "status": "General Docket - At Issue (Ready for Trial)", + "entityName": "UserCase" }, { "sk": "case|58c1f7a3-8062-42f0-a73e-8bd69b419878", @@ -328,6 +357,7 @@ "caseId": "58c1f7a3-8062-42f0-a73e-8bd69b419878", "sk": "document|35306555-83f2-4f61-9be6-438bd107e5eb", "documentId": "35306555-83f2-4f61-9be6-438bd107e5eb", + "numberOfPages": 1, "pk": "case|58c1f7a3-8062-42f0-a73e-8bd69b419878", "status": "served" }, @@ -351,6 +381,7 @@ "filingDate": "2019-08-16T17:29:10.132Z", "filedBy": "Tatum Craig, Wayne Obrien, Partnership Representative", "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "receivedAt": "2019-08-16T17:29:10.132Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" }, @@ -390,6 +421,7 @@ "caseId": "58c1f7a3-8062-42f0-a73e-8bd69b419878", "sk": "document|5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", "documentId": "5bd2f4eb-e08a-41e4-8d18-13b9ffd4514c", + "numberOfPages": 1, "servedAt": "2019-08-16T17:30:10.526Z", "pk": "case|58c1f7a3-8062-42f0-a73e-8bd69b419878", "status": "served" @@ -409,6 +441,7 @@ "caseId": "58c1f7a3-8062-42f0-a73e-8bd69b419878", "sk": "document|7a923abd-fc41-407a-b76b-7f724fa5d47f", "documentId": "7a923abd-fc41-407a-b76b-7f724fa5d47f", + "numberOfPages": 1, "pk": "case|58c1f7a3-8062-42f0-a73e-8bd69b419878", "status": "served" } diff --git a/web-api/storage/fixtures/seed/108-19.json b/web-api/storage/fixtures/seed/108-19.json index 10a1227533d..c2166ee71ba 100644 --- a/web-api/storage/fixtures/seed/108-19.json +++ b/web-api/storage/fixtures/seed/108-19.json @@ -13,6 +13,7 @@ "filedBy": "Garrett Carpenter, Leslie Bullock, Trustee", "workItems": [], "documentId": "3c777b95-f7fa-4826-9a69-9e2c0ab7fa9c", + "numberOfPages": 1, "receivedAt": "2019-08-16T19:21:46.146Z", "filingDate": "2019-08-16T19:21:46.146Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -99,6 +100,7 @@ "eventCode": "P", "index": 1, "documentId": "3c777b95-f7fa-4826-9a69-9e2c0ab7fa9c", + "numberOfPages": 1, "filingDate": "2019-08-16T19:21:46.146Z", "filedBy": "Garrett Carpenter, Leslie Bullock, Trustee", "status": "R served on 08/16/2019 2:22 PM", @@ -134,6 +136,7 @@ "filedBy": "Garrett Carpenter, Leslie Bullock, Trustee", "workItems": [], "documentId": "3c777b95-f7fa-4826-9a69-9e2c0ab7fa9c", + "numberOfPages": 1, "receivedAt": "2019-08-16T19:21:46.146Z", "filingDate": "2019-08-16T19:21:46.146Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -180,6 +183,7 @@ "filedBy": "Garrett Carpenter, Leslie Bullock, Trustee", "workItems": [], "documentId": "3c777b95-f7fa-4826-9a69-9e2c0ab7fa9c", + "numberOfPages": 1, "receivedAt": "2019-08-16T19:21:46.146Z", "filingDate": "2019-08-16T19:21:46.146Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -218,7 +222,15 @@ }, { "sk": "case|46c4064f-b44a-4ac3-9dfb-9ce9f00e43f5", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|46c4064f-b44a-4ac3-9dfb-9ce9f00e43f5", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-08-16T19:21:46.146Z", + "caseCaption": "Garrett Carpenter, Leslie Bullock, Trustee, Petitioner(s)", + "caseId": "46c4064f-b44a-4ac3-9dfb-9ce9f00e43f5", + "docketNumber": "108-19", + "docketNumberWithSuffix": "108-19", + "status": "Calendared", + "entityName": "UserCase" }, { "sk": "work-item|ba1c4ce7-6def-4eb8-9ac7-be844ba3a380", @@ -239,6 +251,7 @@ "caseId": "46c4064f-b44a-4ac3-9dfb-9ce9f00e43f5", "sk": "document|3adb712b-d0c5-40e8-98c1-d2f38757e27a", "documentId": "3adb712b-d0c5-40e8-98c1-d2f38757e27a", + "numberOfPages": 1, "pk": "case|46c4064f-b44a-4ac3-9dfb-9ce9f00e43f5", "status": "served" }, @@ -263,6 +276,7 @@ "filedBy": "Garrett Carpenter, Leslie Bullock, Trustee", "documentId": "3c777b95-f7fa-4826-9a69-9e2c0ab7fa9c", "receivedAt": "2019-08-16T19:21:46.146Z", + "numberOfPages": 1, "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" }, "section": "irsSystem", @@ -301,6 +315,7 @@ "caseId": "46c4064f-b44a-4ac3-9dfb-9ce9f00e43f5", "sk": "document|3c777b95-f7fa-4826-9a69-9e2c0ab7fa9c", "documentId": "3c777b95-f7fa-4826-9a69-9e2c0ab7fa9c", + "numberOfPages": 1, "servedAt": "2019-08-16T19:22:13.925Z", "pk": "case|46c4064f-b44a-4ac3-9dfb-9ce9f00e43f5", "status": "served" diff --git a/web-api/storage/fixtures/seed/109-19.json b/web-api/storage/fixtures/seed/109-19.json index 0b99e293348..09a029272fb 100644 --- a/web-api/storage/fixtures/seed/109-19.json +++ b/web-api/storage/fixtures/seed/109-19.json @@ -56,6 +56,7 @@ "eventCode": "P", "filedBy": "Test Petitioner", "index": 1, + "numberOfPages": 2, "docketRecordId": "e90247b9-0945-47b2-8cef-601d14be2ff9", "pk": "case|2fb2da8d-4328-4a20-a5d7-b76637e1dc02", "sk": "docket-record|e90247b9-0945-47b2-8cef-601d14be2ff9" @@ -104,6 +105,7 @@ "document": { "createdAt": "2019-03-01T21:40:46.415Z", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "documentTitle": "Petition", "documentType": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416", @@ -141,6 +143,7 @@ "caseId": "2fb2da8d-4328-4a20-a5d7-b76637e1dc02", "sk": "document|1f1aa3f7-e2e3-43e6-885d-4ce341588c76", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "pk": "case|2fb2da8d-4328-4a20-a5d7-b76637e1dc02" }, { @@ -167,6 +170,7 @@ "sk": "document|25100ec6-eeeb-4e88-872f-c99fad1fe6c7", "signedAt": "2019-10-07T14:29:30.288Z", "documentId": "25100ec6-eeeb-4e88-872f-c99fad1fe6c7", + "numberOfPages": 1, "signedByUserId": "1805d1ab-18d0-43ec-bafb-654e83405416", "pk": "case|2fb2da8d-4328-4a20-a5d7-b76637e1dc02", "documentTitle": "Order of Dismissal for Lack of Jurisdiction", @@ -187,6 +191,7 @@ "caseId": "2fb2da8d-4328-4a20-a5d7-b76637e1dc02", "sk": "document|b1aa4aa2-c214-424c-8870-d0049c5744d8", "documentId": "b1aa4aa2-c214-424c-8870-d0049c5744d8", + "numberOfPages": 1, "pk": "case|2fb2da8d-4328-4a20-a5d7-b76637e1dc02" } ] diff --git a/web-api/storage/fixtures/seed/110-19.json b/web-api/storage/fixtures/seed/110-19.json index a6b6d8cfc2d..3755fb90b21 100644 --- a/web-api/storage/fixtures/seed/110-19.json +++ b/web-api/storage/fixtures/seed/110-19.json @@ -53,6 +53,7 @@ "eventCode": "P", "filedBy": "Test Petitioner", "index": 1, + "numberOfPages": 2, "docketRecordId": "ca9d204a-5ff6-4915-a31b-94e3d016e4a6", "pk": "case|2fb2da8d-4328-4a20-a5d7-a76637e1dc01", "sk": "docket-record|ca9d204a-5ff6-4915-a31b-94e3d016e4a6" @@ -86,6 +87,7 @@ "documentType": "Petition", "filedBy": "Test Petitioner", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "documentTitle": "Petition", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" }, @@ -121,6 +123,7 @@ "caseId": "2fb2da8d-4328-4a20-a5d7-a76637e1dc01", "sk": "document|1f1aa3f7-e2e3-43e6-885d-4ce341588c76", "documentId": "1f1aa3f7-e2e3-43e6-885d-4ce341588c76", + "numberOfPages": 1, "pk": "case|2fb2da8d-4328-4a20-a5d7-a76637e1dc01" }, { @@ -147,6 +150,7 @@ "sk": "document|25100ec6-eeeb-4e88-872f-c99fad1fe6c7", "signedAt": "2019-10-07T14:29:30.288Z", "documentId": "25100ec6-eeeb-4e88-872f-c99fad1fe6c7", + "numberOfPages": 1, "signedByUserId": "1805d1ab-18d0-43ec-bafb-654e83405416", "pk": "case|2fb2da8d-4328-4a20-a5d7-a76637e1dc01", "documentTitle": "Order of Dismissal for Lack of Jurisdiction", @@ -167,6 +171,7 @@ "caseId": "2fb2da8d-4328-4a20-a5d7-a76637e1dc01", "sk": "document|b1aa4aa2-c214-424c-8870-d0049c5744d8", "documentId": "b1aa4aa2-c214-424c-8870-d0049c5744d8", + "numberOfPages": 1, "pk": "case|2fb2da8d-4328-4a20-a5d7-a76637e1dc01" } ] diff --git a/web-api/storage/fixtures/seed/111-19.json b/web-api/storage/fixtures/seed/111-19.json index 2a9d489d66c..0d7a3c8df1d 100644 --- a/web-api/storage/fixtures/seed/111-19.json +++ b/web-api/storage/fixtures/seed/111-19.json @@ -51,6 +51,7 @@ "eventCode": "P", "index": 1, "documentId": "9de27a7d-7c6b-434b-803b-7655f82d5e07", + "numberOfPages": 1, "filingDate": "2019-12-11T15:25:09.285Z", "filedBy": "Petr. Guy Fieri", "docketRecordId": "2bdfa4ce-2ebe-4b95-9965-ddec14c67e3c", @@ -72,7 +73,16 @@ }, { "sk": "case|fd8d1139-5e54-4117-9b94-7a69359423c2", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|fd8d1139-5e54-4117-9b94-7a69359423c2", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-12-11T15:25:09.284Z", + "caseCaption": "Guy Fieri, Petitioner", + "caseId": "fd8d1139-5e54-4117-9b94-7a69359423c2", + "leadCaseId": "fd8d1139-5e54-4117-9b94-7a69359423c2", + "docketNumber": "111-19", + "docketNumberWithSuffix": "111-19L", + "status": "Submitted", + "entityName": "UserCase" }, { "documentType": "Statement of Taxpayer Identification", @@ -90,6 +100,7 @@ "caseId": "fd8d1139-5e54-4117-9b94-7a69359423c2", "sk": "document|59ccf18f-5bac-48b0-a8e0-9c9e1d995b62", "partySecondary": false, + "numberOfPages": 1, "documentId": "59ccf18f-5bac-48b0-a8e0-9c9e1d995b62", "pk": "case|fd8d1139-5e54-4117-9b94-7a69359423c2" }, @@ -114,6 +125,7 @@ "partyPrimary": true, "pending": false, "partySecondary": false, + "numberOfPages": 1, "documentId": "9de27a7d-7c6b-434b-803b-7655f82d5e07", "receivedAt": "2019-12-11T15:25:09.285Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" @@ -124,7 +136,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-12-11T15:25:09.285Z", "assigneeName": null, "caseId": "fd8d1139-5e54-4117-9b94-7a69359423c2", @@ -152,6 +165,7 @@ "sk": "document|9de27a7d-7c6b-434b-803b-7655f82d5e07", "partySecondary": false, "documentId": "9de27a7d-7c6b-434b-803b-7655f82d5e07", + "numberOfPages": 1, "pk": "case|fd8d1139-5e54-4117-9b94-7a69359423c2" } ] diff --git a/web-api/storage/fixtures/seed/112-19.json b/web-api/storage/fixtures/seed/112-19.json index 2b14a27fd6c..ffdf7355462 100644 --- a/web-api/storage/fixtures/seed/112-19.json +++ b/web-api/storage/fixtures/seed/112-19.json @@ -51,6 +51,7 @@ "description": "Petition", "index": 1, "documentId": "337b0684-0a8e-43fd-a9e5-3a01c8158cc8", + "numberOfPages": 1, "filingDate": "2019-12-11T15:25:55.007Z", "eventCode": "P", "filedBy": "Petr. Brian Earl Spilner", @@ -73,7 +74,16 @@ }, { "sk": "case|459925e4-db20-46e0-8348-47a98537edeb", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|459925e4-db20-46e0-8348-47a98537edeb", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-12-11T15:25:55.006Z", + "caseCaption": "Brian Earl Spilner, Petitioner", + "caseId": "459925e4-db20-46e0-8348-47a98537edeb", + "leadCaseId": "fd8d1139-5e54-4117-9b94-7a69359423c2", + "docketNumber": "112-19", + "docketNumberWithSuffix": "112-19L", + "status": "Submitted", + "entityName": "UserCase" }, { "documentType": "Statement of Taxpayer Identification", @@ -92,6 +102,7 @@ "sk": "document|1c56d0fc-b01b-4bc7-830f-0c25b4e6b803", "partySecondary": false, "documentId": "1c56d0fc-b01b-4bc7-830f-0c25b4e6b803", + "numberOfPages": 1, "pk": "case|459925e4-db20-46e0-8348-47a98537edeb" }, { @@ -116,6 +127,7 @@ "pending": false, "partySecondary": false, "documentId": "337b0684-0a8e-43fd-a9e5-3a01c8158cc8", + "numberOfPages": 1, "receivedAt": "2019-12-11T15:25:55.007Z", "userId": "7805d1ab-18d0-43ec-bafb-654e83405416" }, @@ -125,7 +137,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-12-11T15:25:55.008Z", "assigneeName": null, "caseId": "459925e4-db20-46e0-8348-47a98537edeb", @@ -153,6 +166,7 @@ "sk": "document|337b0684-0a8e-43fd-a9e5-3a01c8158cc8", "partySecondary": false, "documentId": "337b0684-0a8e-43fd-a9e5-3a01c8158cc8", + "numberOfPages": 1, "pk": "case|459925e4-db20-46e0-8348-47a98537edeb" } ] diff --git a/web-api/storage/fixtures/seed/113-19.json b/web-api/storage/fixtures/seed/113-19.json index 7628927d661..42471da76ee 100644 --- a/web-api/storage/fixtures/seed/113-19.json +++ b/web-api/storage/fixtures/seed/113-19.json @@ -51,6 +51,7 @@ "eventCode": "P", "index": 1, "documentId": "6d83425c-8ef3-4c66-b776-6c7957c53f4d", + "numberOfPages": 1, "filingDate": "2019-12-11T16:02:31.174Z", "filedBy": "Petr. Letti Toretto", "docketRecordId": "e4932867-0312-4f82-a1d2-2ddba4679db6", @@ -63,6 +64,7 @@ "index": 2, "filingDate": "2019-12-11T16:02:31.173Z", "docketRecordId": "4760bafd-e75e-4924-8072-bdbef4739011", + "pk": "case|cbe6e7dc-a209-4d5f-90d1-9bf7025b705b", "sk": "docket-record|4760bafd-e75e-4924-8072-bdbef4739011" }, @@ -72,7 +74,16 @@ }, { "sk": "case|cbe6e7dc-a209-4d5f-90d1-9bf7025b705b", - "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416" + "gsi1pk": "user-case|cbe6e7dc-a209-4d5f-90d1-9bf7025b705b", + "pk": "user|7805d1ab-18d0-43ec-bafb-654e83405416", + "createdAt": "2019-12-11T16:02:31.173Z", + "caseCaption": "Letti Toretto, Petitioner", + "caseId": "cbe6e7dc-a209-4d5f-90d1-9bf7025b705b", + "leadCaseId": "fd8d1139-5e54-4117-9b94-7a69359423c2", + "docketNumber": "113-19", + "docketNumberWithSuffix": "113-19L", + "status": "Submitted", + "entityName": "UserCase" }, { "documentType": "Statement of Taxpayer Identification", @@ -91,6 +102,7 @@ "sk": "document|3cf1f474-1dcd-47aa-8455-8f9a93d80183", "partySecondary": false, "documentId": "3cf1f474-1dcd-47aa-8455-8f9a93d80183", + "numberOfPages": 1, "pk": "case|cbe6e7dc-a209-4d5f-90d1-9bf7025b705b" }, { @@ -124,7 +136,8 @@ "isInitializeCase": true, "assigneeId": null, "isQC": true, - "sentBy": "7805d1ab-18d0-43ec-bafb-654e83405416", + "sentBy": "Test Petitioner", + "sentByUserId": "7805d1ab-18d0-43ec-bafb-654e83405416", "createdAt": "2019-12-11T16:02:31.175Z", "assigneeName": null, "caseId": "cbe6e7dc-a209-4d5f-90d1-9bf7025b705b", @@ -152,6 +165,7 @@ "sk": "document|6d83425c-8ef3-4c66-b776-6c7957c53f4d", "partySecondary": false, "documentId": "6d83425c-8ef3-4c66-b776-6c7957c53f4d", + "numberOfPages": 1, "pk": "case|cbe6e7dc-a209-4d5f-90d1-9bf7025b705b" } ] diff --git a/web-api/storage/fixtures/seed/case-messages.json b/web-api/storage/fixtures/seed/case-messages.json new file mode 100644 index 00000000000..97a43c68ed9 --- /dev/null +++ b/web-api/storage/fixtures/seed/case-messages.json @@ -0,0 +1,23 @@ +[ + { + "attachments": [], + "caseId": "0bc59d5b-c2b9-41f7-92a7-03b8cadffcc0", + "caseStatus": "General Docket - Not at Issue", + "createdAt": "2020-06-05T18:02:25.280Z", + "docketNumber": "105-20", + "docketNumberWithSuffix": "105-20L", + "entityName": "CaseMessage", + "from": "Test Petitionsclerk", + "fromSection": "petitions", + "fromUserId": "3805d1ab-18d0-43ec-bafb-654e83405416", + "gsi1pk": "message|eb0a139a-8951-4de1-8b83-f02a27504105", + "message": "hello!", + "messageId": "eb0a139a-8951-4de1-8b83-f02a27504105", + "pk": "case|0bc59d5b-c2b9-41f7-92a7-03b8cadffcc0", + "sk": "message|eb0a139a-8951-4de1-8b83-f02a27504105", + "subject": "message to myself", + "to": "Test Petitionsclerk", + "toSection": "petitions", + "toUserId": "3805d1ab-18d0-43ec-bafb-654e83405416" + } +] diff --git a/web-api/storage/fixtures/seed/fix.js b/web-api/storage/fixtures/seed/fix.js index b8045d985a2..47b49e9b373 100644 --- a/web-api/storage/fixtures/seed/fix.js +++ b/web-api/storage/fixtures/seed/fix.js @@ -20,6 +20,7 @@ const files = [ ]; for (const file of files) { + // eslint-disable-next-line security/detect-non-literal-require let json = require(file); const items = []; for (const item of json) { diff --git a/web-api/storage/fixtures/seed/index.js b/web-api/storage/fixtures/seed/index.js index ae4c648ba9c..1e703409d9d 100755 --- a/web-api/storage/fixtures/seed/index.js +++ b/web-api/storage/fixtures/seed/index.js @@ -17,6 +17,7 @@ module.exports = [ ...require('./103-20.json'), ...require('./104-20.json'), ...require('./105-20.json'), + ...require('./case-messages.json'), ...require('./trial-sessions.json'), ...require('./trial-sessions-past.json'), ...require('./misc.json'), diff --git a/web-api/storage/fixtures/seed/users.json b/web-api/storage/fixtures/seed/users.json index 33eeac393e8..5e1f9be0170 100755 --- a/web-api/storage/fixtures/seed/users.json +++ b/web-api/storage/fixtures/seed/users.json @@ -153,7 +153,7 @@ "entityName": "PrivatePractitioner", "email": "privatePractitioner", "firmName": "GW Law Offices", - "alternateEmail": "privatePractitioner@example.com", + "alternateEmail": "privatePractitioner", "additionalPhone": "111-111-1111", "originalBarState": "Maryland", "barNumber": "PT1234", diff --git a/web-api/storage/fixtures/validate-seed.js b/web-api/storage/fixtures/validate-seed.js index 4b312eaeeea..1755c569c81 100755 --- a/web-api/storage/fixtures/validate-seed.js +++ b/web-api/storage/fixtures/validate-seed.js @@ -25,10 +25,9 @@ stdin.on('end', () => { /** * @param {Array} entry to the seedFile which ought to exist - * @param {string} entry.seedFile the seed file that referenced the documentId * @param {string} entry.uuid the documentId's UUID */ -function checkFilesExist([uuid, seedFile]) { +function checkFilesExist([uuid]) { const createFiles = { [`${__dirname}/s3/noop-documents-local-us-east-1/${uuid}._S3rver_metadata.json`]: `${__dirname}/s3/noop-documents-local-us-east-1/${EXISTING_UUID}._S3rver_metadata.json`, [`${__dirname}/s3/noop-documents-local-us-east-1/${uuid}._S3rver_object`]: `${__dirname}/s3/noop-documents-local-us-east-1/${EXISTING_UUID}._S3rver_object`, diff --git a/web-api/storage/scripts/createUsers.js b/web-api/storage/scripts/createUsers.js index 1c4d58f1411..8e1c5dfb83e 100644 --- a/web-api/storage/scripts/createUsers.js +++ b/web-api/storage/scripts/createUsers.js @@ -6,8 +6,10 @@ const { const { createUserRecords: createPractitionerUserRecords, } = require('../../../shared/src/persistence/dynamo/users/createPractitionerUser.js'); +const { + ROLES, +} = require('../../../shared/src/business/entities/EntityConstants'); const { omit } = require('lodash'); -const { User } = require('../../../shared/src/business/entities/User'); let usersByEmail = {}; @@ -32,9 +34,9 @@ module.exports.createUsers = async () => { if ( [ - User.ROLES.irsPractitioner, - User.ROLES.privatePractitioner, - User.ROLES.inactivePractitioner, + ROLES.irsPractitioner, + ROLES.privatePractitioner, + ROLES.inactivePractitioner, ].includes(userRecord.role) ) { return createPractitionerUserRecords({ diff --git a/web-api/storage/scripts/loadTest/loadTestHelpers.js b/web-api/storage/scripts/loadTest/loadTestHelpers.js index 20699532fc3..5a40a9a8440 100644 --- a/web-api/storage/scripts/loadTest/loadTestHelpers.js +++ b/web-api/storage/scripts/loadTest/loadTestHelpers.js @@ -1,9 +1,10 @@ const faker = require('faker'); const { - TrialSession, -} = require('../../../../shared/src/business/entities/trialSessions/TrialSession'); -const { Case } = require('../../../../shared/src/business/entities/cases/Case'); -const { User } = require('../../../../shared/src/business/entities/User'); + FILING_TYPES, + PROCEDURE_TYPES, + ROLES, + TRIAL_CITY_STRINGS, +} = require('../../../../shared/src/business/entities/EntityConstants'); const createTrialSession = async ({ applicationContext }) => { let startDate = faker.date.future(1); @@ -35,9 +36,7 @@ const createTrialSession = async ({ applicationContext }) => { term = 'Fall'; } - let trialLocation = faker.random.arrayElement( - TrialSession.TRIAL_CITY_STRINGS, - ); + let trialLocation = faker.random.arrayElement(TRIAL_CITY_STRINGS); return await applicationContext.getUseCases().createTrialSessionInteractor({ applicationContext, @@ -129,15 +128,11 @@ const createCase = async ({ postalCode: faker.address.zipCode(), state: faker.address.stateAbbr(), }, - filingType: faker.random.arrayElement( - Case.FILING_TYPES[User.ROLES.petitioner], - ), + filingType: faker.random.arrayElement(FILING_TYPES[ROLES.petitioner]), hasIrsNotice: false, partyType: 'Petitioner', - preferredTrialCity: faker.random.arrayElement( - TrialSession.TRIAL_CITY_STRINGS, - ), - procedureType: faker.random.arrayElement(Case.PROCEDURE_TYPES), + preferredTrialCity: faker.random.arrayElement(TRIAL_CITY_STRINGS), + procedureType: faker.random.arrayElement(PROCEDURE_TYPES), }, stinFileId, }); diff --git a/web-api/storage/scripts/loadTest/loadTestPractitioners.js b/web-api/storage/scripts/loadTest/loadTestPractitioners.js index 05f4ba4199a..1afbc9fac2a 100644 --- a/web-api/storage/scripts/loadTest/loadTestPractitioners.js +++ b/web-api/storage/scripts/loadTest/loadTestPractitioners.js @@ -12,7 +12,7 @@ const cognito = new AWS.CognitoIdentityServiceProvider({ (async () => { let practitionerUser; - var apigateway = new AWS.APIGateway({ + const apigateway = new AWS.APIGateway({ region: process.env.REGION, }); const { items: apis } = await apigateway @@ -28,14 +28,14 @@ const cognito = new AWS.CognitoIdentityServiceProvider({ return obj; }, {}); - let token = await getUserToken({ + const token = await getUserToken({ cognito, env: process.env.ENV, password: 'Testing1234$', username: 'practitioner1@example.com', }); - let response = await axios.get(`${services['ef-cms-users-green']}`, { + const response = await axios.get(`${services['ef-cms-users-green']}`, { headers: { Authorization: `Bearer ${token}`, }, diff --git a/web-api/storage/scripts/loadTest/loadTestTrialSession.js b/web-api/storage/scripts/loadTest/loadTestTrialSession.js index 5996e7c2646..67a77d8d42b 100644 --- a/web-api/storage/scripts/loadTest/loadTestTrialSession.js +++ b/web-api/storage/scripts/loadTest/loadTestTrialSession.js @@ -18,7 +18,7 @@ const cognito = new AWS.CognitoIdentityServiceProvider({ let petitionerUser; let docketClerkUser; - var apigateway = new AWS.APIGateway({ + const apigateway = new AWS.APIGateway({ region: process.env.REGION, }); const { items: apis } = await apigateway diff --git a/web-api/swagger.json b/web-api/swagger.json index c72ec087807..ddb632fd79f 100644 --- a/web-api/swagger.json +++ b/web-api/swagger.json @@ -1065,19 +1065,11 @@ } } }, - "/public-api/cases/{caseId}": { - "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - } - ], + "/cases/open": { "get": { "tags": ["cases"], - "summary": "get a case by caseId", - "description": "Get a case by caseId.\n", + "summary": "Retrieve open cases", + "description": "Retrieve open cases.\n", "produces": ["application/json"], "responses": { "200": { @@ -1117,19 +1109,11 @@ } } }, - "/case-meta/{caseId}/block": { - "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - } - ], - "post": { + "/cases/closed": { + "get": { "tags": ["cases"], - "summary": "add a block to a case", - "description": "add a block to a case.\n", + "summary": "Retrieve closed cases", + "description": "Retrieve closed cases.\n", "produces": ["application/json"], "responses": { "200": { @@ -1145,10 +1129,43 @@ } ] }, - "delete": { + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/public-api/cases/{caseId}": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "get": { "tags": ["cases"], - "summary": "removed the block from a case", - "description": "removed the block from a case.\n", + "summary": "get a case by caseId", + "description": "Get a case by caseId.\n", "produces": ["application/json"], "responses": { "200": { @@ -1188,25 +1205,19 @@ } } }, - "/cases/{caseId}/remove-pending/{documentId}": { + "/public-api/docket-number-search/{docketNumber}": { "parameters": [ { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "documentId", + "name": "docketNumber", "in": "path", "required": true, "type": "string" } ], - "delete": { + "get": { "tags": ["cases"], - "summary": "removes a pending item from a case", - "description": "removes a pending item from a case.\n", + "summary": "get a case by docket number", + "description": "Get a case by docketNumber.\n", "produces": ["application/json"], "responses": { "200": { @@ -1246,7 +1257,7 @@ } } }, - "/case-meta/{caseId}/high-priority": { + "/case-meta/{caseId}/block": { "parameters": [ { "name": "caseId", @@ -1257,8 +1268,8 @@ ], "post": { "tags": ["cases"], - "summary": "sets a case as high priority", - "description": "sets a case as high priority.\n", + "summary": "add a block to a case", + "description": "add a block to a case.\n", "produces": ["application/json"], "responses": { "200": { @@ -1276,8 +1287,8 @@ }, "delete": { "tags": ["cases"], - "summary": "removes the high priority from a case", - "description": "removes the high priority from a case.\n", + "summary": "removed the block from a case", + "description": "removed the block from a case.\n", "produces": ["application/json"], "responses": { "200": { @@ -1317,21 +1328,26 @@ } } }, - "/case-meta/{caseId}/case-context": { + "/cases/{caseId}/remove-pending/{documentId}": { "parameters": [ { "name": "caseId", "in": "path", "required": true, "type": "string" + }, + { + "name": "documentId", + "in": "path", + "required": true, + "type": "string" } ], - "put": { + "delete": { "tags": ["cases"], - "summary": "Updates the caption, status, and judge on the given case.", - "description": "Updates the caption, status, and judge on the given case.\n", + "summary": "removes a pending item from a case", + "description": "removes a pending item from a case.\n", "produces": ["application/json"], - "consumes": ["application/json"], "responses": { "200": { "description": "200 response", @@ -1370,21 +1386,26 @@ } } }, - "/case-meta/{caseId}/consolidate-case": { + "/cases/{caseId}/statistics/{statisticId}": { "parameters": [ { "name": "caseId", "in": "path", "required": true, "type": "string" + }, + { + "name": "statisticId", + "in": "path", + "required": true, + "type": "string" } ], "put": { "tags": ["cases"], - "summary": "Consolidates two or more cases associated with the given case ids.", - "description": "Consolidates two or more cases associated with the given case ids.\n", + "summary": "updates statistics on a case", + "description": "updates statistics on a case.\n", "produces": ["application/json"], - "consumes": ["application/json"], "responses": { "200": { "description": "200 response", @@ -1400,19 +1421,10 @@ ] }, "delete": { - "parameters": [ - { - "name": "caseIdsToRemove", - "in": "query", - "required": true, - "type": "string" - } - ], "tags": ["cases"], - "summary": "Unconsolidates the cases passed in the query string.", - "description": "Unconsolidates the cases passed in the query string.\n", + "summary": "removes a statistics entry off a case", + "description": "removes a statistics entry off a case.\n", "produces": ["application/json"], - "consumes": ["application/json"], "responses": { "200": { "description": "200 response", @@ -1451,7 +1463,7 @@ } } }, - "/cases/{caseId}/consolidated-cases": { + "/case-meta/{caseId}/high-priority": { "parameters": [ { "name": "caseId", @@ -1460,12 +1472,11 @@ "type": "string" } ], - "get": { + "post": { "tags": ["cases"], - "summary": "Returns all consolidated cases associated with the given case id.", - "description": "Returns all consolidated cases associated with the given case id.\n", + "summary": "sets a case as high priority", + "description": "sets a case as high priority.\n", "produces": ["application/json"], - "consumes": ["application/json"], "responses": { "200": { "description": "200 response", @@ -1480,41 +1491,16 @@ } ] }, - "options": { - "consumes": ["application/json"], - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "headers": { - "Access-Control-Allow-Origin": { - "type": "string" - }, - "Access-Control-Allow-Methods": { - "type": "string" - }, - "Access-Control-Allow-Credentials": { - "type": "string" - }, - "Access-Control-Allow-Headers": { - "type": "string" - } - } - } - } - } - }, - "/case-documents/order-search": { - "get": { - "tags": ["case-documents"], - "summary": "search for an order-related document type by keyword within its title or contents", - "description": "Search for an order-related document type by keyword within its title or contents.\n", + "delete": { + "tags": ["cases"], + "summary": "removes the high priority from a case", + "description": "removes the high priority from a case.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/document" + "$ref": "#/definitions/case" } } }, @@ -1548,7 +1534,7 @@ } } }, - "/case-documents/{caseId}/external-document": { + "/case-meta/{caseId}/other-statistics": { "parameters": [ { "name": "caseId", @@ -1558,15 +1544,15 @@ } ], "post": { - "tags": ["documents"], - "summary": "file a new external document on a case", - "description": "Creates a new external document and attaches it to a case. It will also create a work item.\n", + "tags": ["cases"], + "summary": "update other statistics on a case", + "description": "update other statistics on a case.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/document" + "$ref": "#/definitions/case" } } }, @@ -1600,7 +1586,7 @@ } } }, - "/case-documents/{caseId}/docket-entry": { + "/case-meta/{caseId}/statistics": { "parameters": [ { "name": "caseId", @@ -1610,28 +1596,9 @@ } ], "post": { - "tags": ["documents"], - "summary": "file a new docket entry on a case", - "description": "Creates a new docket entry/document and attaches it to a case.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/document" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "put": { - "tags": ["documents"], - "summary": "updates a current docket entry on a case", - "description": "Updates a docket entry/document and attaches it to a case.\n", + "tags": ["cases"], + "summary": "add a statistic to a case", + "description": "add a statistic to a case.\n", "produces": ["application/json"], "responses": { "200": { @@ -1671,7 +1638,7 @@ } } }, - "/case-documents/{caseId}/docket-entry-meta": { + "/case-meta/{caseId}/case-context": { "parameters": [ { "name": "caseId", @@ -1681,10 +1648,11 @@ } ], "put": { - "tags": ["documents", "docket record"], - "summary": "Updates the docket entry meta data on a case", - "description": "Updates the docket entry meta on a case.\n", + "tags": ["cases"], + "summary": "Updates the caption, status, and judge on the given case.", + "description": "Updates the caption, status, and judge on the given case.\n", "produces": ["application/json"], + "consumes": ["application/json"], "responses": { "200": { "description": "200 response", @@ -1723,7 +1691,7 @@ } } }, - "/case-documents/{caseId}/docket-entry-in-progress": { + "/case-meta/{caseId}/consolidate-case": { "parameters": [ { "name": "caseId", @@ -1733,10 +1701,39 @@ } ], "put": { - "tags": ["documents"], - "summary": "updates a current docket entry on a case that is in progress", - "description": "Updates a docket entry on a case that is in progress.\n", + "tags": ["cases"], + "summary": "Consolidates two or more cases associated with the given case ids.", + "description": "Consolidates two or more cases associated with the given case ids.\n", + "produces": ["application/json"], + "consumes": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "delete": { + "parameters": [ + { + "name": "caseIdsToRemove", + "in": "query", + "required": true, + "type": "string" + } + ], + "tags": ["cases"], + "summary": "Unconsolidates the cases passed in the query string.", + "description": "Unconsolidates the cases passed in the query string.\n", "produces": ["application/json"], + "consumes": ["application/json"], "responses": { "200": { "description": "200 response", @@ -1775,7 +1772,7 @@ } } }, - "/case-documents/{caseId}/docket-entry-complete": { + "/cases/{caseId}/consolidated-cases": { "parameters": [ { "name": "caseId", @@ -1784,11 +1781,12 @@ "type": "string" } ], - "put": { - "tags": ["documents"], - "summary": "completes a docket entry on a case that was previously in progress", - "description": "Completes a docket entry on a case that was previously in progress.\n", + "get": { + "tags": ["cases"], + "summary": "Returns all consolidated cases associated with the given case id.", + "description": "Returns all consolidated cases associated with the given case id.\n", "produces": ["application/json"], + "consumes": ["application/json"], "responses": { "200": { "description": "200 response", @@ -1827,64 +1825,17 @@ } } }, - "/cases/{caseId}/irsPetitionPackage": { - "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - } - ], - "post": { - "tags": ["cases"], - "summary": "sends the case to the holding queue", - "description": "Send a packaged case to the respondent.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/case" - } - }, - "422": { - "description": "422 response", - "schema": { - "$ref": "#/definitions/unprocessableEntityError" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "delete": { - "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": ["cases"], - "summary": "deletes the petition on the case from the holding queue", - "description": "Deletes the petition on the case from the holding queue.\n", + "/case-documents/order-search": { + "get": { + "tags": ["case-documents"], + "summary": "search for an order-related document type by keyword within its title or contents", + "description": "Search for an order-related document type by keyword within its title or contents.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/case" - } - }, - "422": { - "description": "422 response", - "schema": { - "$ref": "#/definitions/unprocessableEntityError" + "$ref": "#/definitions/document" } } }, @@ -1918,17 +1869,25 @@ } } }, - "/documents/upload-policy": { - "get": { + "/case-documents/{caseId}/external-document": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "post": { "tags": ["documents"], - "summary": "create a pre-signed url for uploads", - "description": "Create a pre-signed url for document uploads to S3.\n", + "summary": "file a new external document on a case", + "description": "Creates a new external document and attaches it to a case. It will also create a work item.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/policyUrl" + "$ref": "#/definitions/document" } } }, @@ -1962,34 +1921,52 @@ } } }, - "/case-documents/{caseId}/{documentId}/document-download-url": { + "/case-documents/{caseId}/docket-entry": { "parameters": [ { "name": "caseId", "in": "path", "required": true, "type": "string" - }, - { - "name": "documentId", - "in": "path", - "required": true, - "type": "string" } ], - "get": { + "post": { "tags": ["documents"], - "summary": "redirects to the s3 url for downloading a document", - "description": "Create and redirects the user to a pre-signed url for document downloads from S3.\n", + "summary": "file a new docket entry on a case", + "description": "Creates a new docket entry/document and attaches it to a case.\n", "produces": ["application/json"], "responses": { - "302": { - "description": "302 response", + "200": { + "description": "200 response", "schema": { - "$ref": "#/definitions/policyUrl" + "$ref": "#/definitions/document" } } - } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "put": { + "tags": ["documents"], + "summary": "updates a current docket entry on a case", + "description": "Updates a docket entry/document and attaches it to a case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] }, "options": { "consumes": ["application/json"], @@ -2015,34 +1992,33 @@ } } }, - "/public-api/{caseId}/{documentId}/public-document-download-url": { + "/case-documents/{caseId}/docket-entry-meta": { "parameters": [ { "name": "caseId", "in": "path", "required": true, "type": "string" - }, - { - "name": "documentId", - "in": "path", - "required": true, - "type": "string" } ], - "get": { - "tags": ["documents"], - "summary": "redirects to the s3 url for downloading a public document", - "description": "Create and redirects the user to a pre-signed url for document downloads from S3.\n", + "put": { + "tags": ["documents", "docket record"], + "summary": "Updates the docket entry meta data on a case", + "description": "Updates the docket entry meta on a case.\n", "produces": ["application/json"], "responses": { - "302": { - "description": "302 response", + "200": { + "description": "200 response", "schema": { - "$ref": "#/definitions/policyUrl" + "$ref": "#/definitions/case" } } - } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] }, "options": { "consumes": ["application/json"], @@ -2068,14 +2044,8 @@ } } }, - "/case-documents/{caseId}/{documentId}/download-policy-url": { + "/case-documents/{caseId}/docket-entry-in-progress": { "parameters": [ - { - "name": "documentId", - "in": "path", - "required": true, - "type": "string" - }, { "name": "caseId", "in": "path", @@ -2083,16 +2053,16 @@ "type": "string" } ], - "get": { + "put": { "tags": ["documents"], - "summary": "create a pre-signed url for downloads", - "description": "Create a pre-signed url for document downloads from S3.\n", + "summary": "updates a current docket entry on a case that is in progress", + "description": "Updates a docket entry on a case that is in progress.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/policyUrl" + "$ref": "#/definitions/case" } } }, @@ -2126,23 +2096,26 @@ } } }, - "/documents/{documentId}/virus-scan": { + "/case-documents/{caseId}/docket-entry-complete": { "parameters": [ { - "name": "documentId", + "name": "caseId", "in": "path", "required": true, "type": "string" } ], - "post": { + "put": { "tags": ["documents"], - "summary": "triggers the virus scan function", - "description": "Endpoint that triggers the virus scan function for the specified document.\n", + "summary": "completes a docket entry on a case that was previously in progress", + "description": "Completes a docket entry on a case that was previously in progress.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" + } } }, "security": [ @@ -2175,23 +2148,65 @@ } } }, - "/documents/{documentId}/validate": { + "/cases/{caseId}/irsPetitionPackage": { "parameters": [ { - "name": "documentId", + "name": "caseId", "in": "path", "required": true, "type": "string" } ], "post": { - "tags": ["documents"], - "summary": "triggers the pdf validation function", - "description": "Endpoint that triggers the PDF validation function for the specified document.\n", + "tags": ["cases"], + "summary": "sends the case to the holding queue", + "description": "Send a packaged case to the respondent.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" + } + }, + "422": { + "description": "422 response", + "schema": { + "$ref": "#/definitions/unprocessableEntityError" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "delete": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": ["cases"], + "summary": "deletes the petition on the case from the holding queue", + "description": "Deletes the petition on the case from the holding queue.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" + } + }, + "422": { + "description": "422 response", + "schema": { + "$ref": "#/definitions/unprocessableEntityError" + } } }, "security": [ @@ -2224,25 +2239,17 @@ } } }, - "/irsPractitioners/{respondentId}/cases": { - "parameters": [ - { - "name": "respondentId", - "in": "path", - "required": true, - "type": "string" - } - ], + "/documents/upload-policy": { "get": { - "tags": ["cases"], - "summary": "get all cases for a user", - "description": "Get all cases for a user.\n", + "tags": ["documents"], + "summary": "create a pre-signed url for uploads", + "description": "Create a pre-signed url for document uploads to S3.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/case" + "$ref": "#/definitions/policyUrl" } } }, @@ -2276,33 +2283,34 @@ } } }, - "/sections/{section}/messages/inbox": { + "/case-documents/{caseId}/{documentId}/document-download-url": { "parameters": [ { - "name": "section", + "name": "caseId", "in": "path", "required": true, "type": "string" - } + }, + { + "name": "documentId", + "in": "path", + "required": true, + "type": "string" + } ], "get": { - "tags": ["workitems"], - "summary": "get all workitems in the sections inbox", - "description": "Get all workitems in the sections inbox.\n", + "tags": ["documents"], + "summary": "redirects to the s3 url for downloading a document", + "description": "Create and redirects the user to a pre-signed url for document downloads from S3.\n", "produces": ["application/json"], "responses": { - "200": { - "description": "200 response", + "302": { + "description": "302 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/policyUrl" } } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] + } }, "options": { "consumes": ["application/json"], @@ -2328,33 +2336,34 @@ } } }, - "/sections/{section}/document-qc/inbox": { + "/public-api/{caseId}/{documentId}/public-document-download-url": { "parameters": [ { - "name": "section", + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "documentId", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["workitems"], - "summary": "get the section document qc inbox work items", - "description": "get the section document qc inbox work items.\n", + "tags": ["documents"], + "summary": "redirects to the s3 url for downloading a public document", + "description": "Create and redirects the user to a pre-signed url for document downloads from S3.\n", "produces": ["application/json"], "responses": { - "200": { - "description": "200 response", + "302": { + "description": "302 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/policyUrl" } } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] + } }, "options": { "consumes": ["application/json"], @@ -2380,25 +2389,31 @@ } } }, - "/sections/{section}/document-qc/served": { + "/case-documents/{caseId}/{documentId}/download-policy-url": { "parameters": [ { - "name": "section", + "name": "documentId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "caseId", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["workitems"], - "summary": "get the section document qc served work items", - "description": "get the section document qc served work items.\n", + "tags": ["documents"], + "summary": "create a pre-signed url for downloads", + "description": "Create a pre-signed url for document downloads from S3.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/policyUrl" } } }, @@ -2432,14 +2447,8 @@ } } }, - "/case-documents/{caseId}/{documentId}/work-items": { + "/documents/{documentId}/virus-scan": { "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - }, { "name": "documentId", "in": "path", @@ -2448,16 +2457,13 @@ } ], "post": { - "tags": ["workitems"], - "summary": "creates a new work item and attaches it to the case document", - "description": "creates a new work item and attaches it to the case document\n", + "tags": ["documents"], + "summary": "triggers the virus scan function", + "description": "Endpoint that triggers the virus scan function for the specified document.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/workItem" - } + "description": "200 response" } }, "security": [ @@ -2490,14 +2496,8 @@ } } }, - "/case-documents/{caseId}/{documentId}/serve-court-issued": { + "/documents/{documentId}/validate": { "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - }, { "name": "documentId", "in": "path", @@ -2506,16 +2506,13 @@ } ], "post": { - "tags": ["cases"], - "summary": "serves a court issued document", - "description": "serves a court issued document\n", + "tags": ["documents"], + "summary": "triggers the pdf validation function", + "description": "Endpoint that triggers the PDF validation function for the specified document.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/case" - } + "description": "200 response" } }, "security": [ @@ -2548,25 +2545,25 @@ } } }, - "/sections/{section}/messages/sent": { + "/irsPractitioners/{respondentId}/cases": { "parameters": [ { - "name": "section", + "name": "respondentId", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["workitems"], - "summary": "get all workitems in the sections outbox", - "description": "Get all workitems in the sections outbox.\n", + "tags": ["cases"], + "summary": "get all cases for a user", + "description": "Get all cases for a user.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/case" } } }, @@ -2600,7 +2597,7 @@ } } }, - "/sections/{section}/users": { + "/sections/{section}/messages/inbox": { "parameters": [ { "name": "section", @@ -2611,14 +2608,14 @@ ], "get": { "tags": ["workitems"], - "summary": "get all users in a section", - "description": "get all users in a section\n", + "summary": "get all workitems in the sections inbox", + "description": "Get all workitems in the sections inbox.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/workItem" } } }, @@ -2652,42 +2649,25 @@ } } }, - "/users": { + "/sections/{section}/document-qc/inbox": { + "parameters": [ + { + "name": "section", + "in": "path", + "required": true, + "type": "string" + } + ], "get": { - "tags": ["user"], - "summary": "gets user data", - "description": "gets user data.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/user" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "post": { - "tags": ["user"], - "summary": "creates a user", - "description": "creates a user.\n", + "tags": ["workitems"], + "summary": "get the section document qc inbox work items", + "description": "get the section document qc inbox work items.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" - } - }, - "422": { - "description": "422 response", - "schema": { - "$ref": "#/definitions/unprocessableEntityError" + "$ref": "#/definitions/workItem" } } }, @@ -2721,44 +2701,25 @@ } } }, - "/practitioners": { + "/sections/{section}/document-qc/served": { "parameters": [ { - "name": "name", - "in": "query", + "name": "section", + "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["practitioners"], - "summary": "gets a practitioner by name", - "description": "Gets a practitioner by name.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/user" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "post": { - "tags": ["practitioners"], - "summary": "creates a practitioner user", - "description": "creates a practitioner user.\n", + "tags": ["workitems"], + "summary": "get the section document qc served work items", + "description": "get the section document qc served work items.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/workItem" } } }, @@ -2792,44 +2753,31 @@ } } }, - "/practitioners/{barNumber}": { + "/case-documents/{caseId}/{documentId}/work-items": { "parameters": [ { - "name": "barNumber", + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "documentId", "in": "path", "required": true, "type": "string" } ], - "get": { - "tags": ["practitioners"], - "summary": "get the practitioner via bar number", - "description": "Get the practitioner via bar number.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/user" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "put": { - "tags": ["practitioners"], - "summary": "updates a practitioner user", - "description": "updates a practitioner user.\n", + "post": { + "tags": ["workitems"], + "summary": "creates a new work item and attaches it to the case document", + "description": "creates a new work item and attaches it to the case document\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/workItem" } } }, @@ -2863,17 +2811,31 @@ } } }, - "/users/internal": { - "get": { - "tags": ["workitems"], - "summary": "get all internal users", - "description": "get all internal users\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/user" + "/case-documents/{caseId}/{documentId}/serve-court-issued": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "documentId", + "in": "path", + "required": true, + "type": "string" + } + ], + "post": { + "tags": ["cases"], + "summary": "serves a court issued document", + "description": "serves a court issued document\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" } } }, @@ -2907,25 +2869,25 @@ } } }, - "/users/{userId}/cases": { + "/sections/{section}/messages/sent": { "parameters": [ { - "name": "userId", + "name": "section", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["cases"], - "summary": "get all cases for a user", - "description": "Get all cases for a user.\n", + "tags": ["workitems"], + "summary": "get all workitems in the sections outbox", + "description": "Get all workitems in the sections outbox.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/case" + "$ref": "#/definitions/workItem" } } }, @@ -2959,45 +2921,26 @@ } } }, - "/users/{userId}/case/{caseId}/pending": { + "/sections/{section}/users": { "parameters": [ { - "name": "userId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "caseId", + "name": "section", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["cases"], - "summary": "verify if user has pending association with the case", - "description": "Verify if user has pending associationwith the case.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response" - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "put": { - "tags": ["cases"], - "summary": "add pending association for practitioner for case if not associated with the case", - "description": "Add pending association for practitioner for case if not associated with the case.\n", + "tags": ["workitems"], + "summary": "get all users in a section", + "description": "get all users in a section\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/user" + } } }, "security": [ @@ -3030,19 +2973,30 @@ } } }, - "/users/{userId}/contact-info": { - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "type": "string" - } - ], - "put": { + "/users": { + "get": { "tags": ["user"], - "summary": "updates a user's contact information", - "description": "updates a user's contact information.\n", + "summary": "gets user data", + "description": "gets user data.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/user" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "post": { + "tags": ["user"], + "summary": "creates a user", + "description": "creates a user.\n", "produces": ["application/json"], "responses": { "200": { @@ -3050,6 +3004,12 @@ "schema": { "$ref": "#/definitions/user" } + }, + "422": { + "description": "422 response", + "schema": { + "$ref": "#/definitions/unprocessableEntityError" + } } }, "security": [ @@ -3082,25 +3042,44 @@ } } }, - "/users/{userId}/document-qc/inbox": { + "/practitioners": { "parameters": [ { - "name": "userId", - "in": "path", + "name": "name", + "in": "query", "required": true, "type": "string" } ], "get": { - "tags": ["workitems"], - "summary": "get the inbox items from the users document qc section", - "description": "get the inbox items from the users document qc section\n", + "tags": ["practitioners"], + "summary": "gets a practitioner by name", + "description": "Gets a practitioner by name.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/user" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "post": { + "tags": ["practitioners"], + "summary": "creates a practitioner user", + "description": "creates a practitioner user.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/user" } } }, @@ -3134,25 +3113,44 @@ } } }, - "/users/{userId}/document-qc/served": { + "/practitioners/{barNumber}": { "parameters": [ { - "name": "userId", + "name": "barNumber", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["workitems"], - "summary": "get the served items from the users document qc section", - "description": "get the served items from the users document qc section\n", + "tags": ["practitioners"], + "summary": "get the practitioner via bar number", + "description": "Get the practitioner via bar number.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/user" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "put": { + "tags": ["practitioners"], + "summary": "updates a practitioner user", + "description": "updates a practitioner user.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/user" } } }, @@ -3186,25 +3184,17 @@ } } }, - "/users/{userId}/messages/inbox": { - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "type": "string" - } - ], + "/users/internal": { "get": { "tags": ["workitems"], - "summary": "get a users work item inbox", - "description": "get a users work item inbox\n", + "summary": "get all internal users", + "description": "get all internal users\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/user" } } }, @@ -3238,7 +3228,7 @@ } } }, - "/users/{userId}/messages/sent": { + "/users/{userId}/cases": { "parameters": [ { "name": "userId", @@ -3248,15 +3238,15 @@ } ], "get": { - "tags": ["workitems"], - "summary": "get a users work item outbox", - "description": "get a users work item outbox\n", + "tags": ["cases"], + "summary": "get all cases for a user", + "description": "Get all cases for a user.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/case" } } }, @@ -3290,18 +3280,45 @@ } } }, - "/work-items": { + "/users/{userId}/case/{caseId}/pending": { + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "get": { + "tags": ["cases"], + "summary": "verify if user has pending association with the case", + "description": "Verify if user has pending associationwith the case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response" + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, "put": { - "tags": ["workitems"], - "summary": "assigns an assigneId to a list of work item ids", - "description": "Get a workitem.\n", + "tags": ["cases"], + "summary": "add pending association for practitioner for case if not associated with the case", + "description": "Add pending association for practitioner for case if not associated with the case.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/workItem" - } + "description": "200 response" } }, "security": [ @@ -3334,44 +3351,25 @@ } } }, - "/work-items/{workItemId}": { + "/users/{userId}/contact-info": { "parameters": [ { - "name": "workItemId", + "name": "userId", "in": "path", "required": true, "type": "string" } ], - "get": { - "tags": ["workitems"], - "summary": "get a workitem by workItemId", - "description": "Get a workitem.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/workItem" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, "put": { - "tags": ["workitems"], - "summary": "update a workitem by workItemId", - "description": "Update a workitem.\n", + "tags": ["user"], + "summary": "updates a user's contact information", + "description": "updates a user's contact information.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/workItem" + "$ref": "#/definitions/user" } } }, @@ -3405,19 +3403,19 @@ } } }, - "/work-items/{workItemId}/assignee": { + "/users/{userId}/document-qc/inbox": { "parameters": [ { - "name": "workItemId", + "name": "userId", "in": "path", "required": true, "type": "string" } ], - "put": { + "get": { "tags": ["workitems"], - "summary": "sets the assigneeId of the workitem to a new user", - "description": "sets the assigneeId of the workitem to a new user.\n", + "summary": "get the inbox items from the users document qc section", + "description": "get the inbox items from the users document qc section\n", "produces": ["application/json"], "responses": { "200": { @@ -3457,29 +3455,26 @@ } } }, - "/case-documents/{caseId}/{documentId}/coversheet": { + "/users/{userId}/document-qc/served": { "parameters": [ { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "documentId", + "name": "userId", "in": "path", "required": true, "type": "string" } ], - "post": { + "get": { "tags": ["workitems"], - "summary": "creates a coversheet and prepends it to the existing s3 document", - "description": "creates a coversheet and prepends it to the existing s3 document.\n", + "summary": "get the served items from the users document qc section", + "description": "get the served items from the users document qc section\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/workItem" + } } }, "security": [ @@ -3512,29 +3507,26 @@ } } }, - "/case-documents/{caseId}/{documentId}/sign": { + "/users/{userId}/messages/inbox": { "parameters": [ { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "documentId", + "name": "userId", "in": "path", "required": true, "type": "string" } ], - "post": { - "tags": ["documents"], - "summary": "creates a copy of an existing document, adds a signature, and adds it to the associated case", - "description": "creates a copy of an existing document, adds a signature, and adds it to the associated case.\n", + "get": { + "tags": ["workitems"], + "summary": "get a users work item inbox", + "description": "get a users work item inbox\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/workItem" + } } }, "security": [ @@ -3567,19 +3559,19 @@ } } }, - "/work-items/{workItemId}/complete": { + "/users/{userId}/messages/sent": { "parameters": [ { - "name": "workItemId", + "name": "userId", "in": "path", "required": true, "type": "string" } ], - "put": { + "get": { "tags": ["workitems"], - "summary": "marks the workitem complete", - "description": "marks the workitem complete.\n", + "summary": "get a users work item outbox", + "description": "get a users work item outbox\n", "produces": ["application/json"], "responses": { "200": { @@ -3619,55 +3611,17 @@ } } }, - "/trial-sessions": { - "get": { - "tags": ["trialsessions"], - "summary": "gets all trial sessions", - "description": "gets all trial sessions.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/trialSession" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "post": { - "tags": ["trialsessions"], - "summary": "creates a new trial session", - "description": "creates a new trial session.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/trialSession" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, + "/work-items": { "put": { - "tags": ["trialsessions"], - "summary": "updates a trial session", - "description": "updates a trial session.\n", + "tags": ["workitems"], + "summary": "assigns an assigneId to a list of work item ids", + "description": "Get a workitem.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/trialSession" + "$ref": "#/definitions/workItem" } } }, @@ -3701,25 +3655,44 @@ } } }, - "/trial-sessions/{trialSessionId}": { + "/work-items/{workItemId}": { "parameters": [ { - "name": "trialSessionId", + "name": "workItemId", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["trialsessions"], - "summary": "gets a trial session", - "description": "gets a trial session.\n", + "tags": ["workitems"], + "summary": "get a workitem by workItemId", + "description": "Get a workitem.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/trialSession" + "$ref": "#/definitions/workItem" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "put": { + "tags": ["workitems"], + "summary": "update a workitem by workItemId", + "description": "Update a workitem.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/workItem" } } }, @@ -3753,25 +3726,25 @@ } } }, - "/trial-sessions/{trialSessionId}/set-swing-session": { + "/work-items/{workItemId}/assignee": { "parameters": [ { - "name": "trialSessionId", + "name": "workItemId", "in": "path", "required": true, "type": "string" } ], - "post": { - "tags": ["trialsessions"], - "summary": "sets a trial session as a swing session", - "description": "sets a trial session as a swing session.\n", + "put": { + "tags": ["workitems"], + "summary": "sets the assigneeId of the workitem to a new user", + "description": "sets the assigneeId of the workitem to a new user.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/trialSession" + "$ref": "#/definitions/workItem" } } }, @@ -3805,26 +3778,29 @@ } } }, - "/trial-sessions/{trialSessionId}/eligible-cases": { + "/case-documents/{caseId}/{documentId}/coversheet": { "parameters": [ { - "name": "trialSessionId", + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "documentId", "in": "path", "required": true, "type": "string" } ], - "get": { - "tags": ["trialsessions"], - "summary": "gets eligible cases for a trial session", - "description": "gets eligible cases for a trial session.\n", + "post": { + "tags": ["workitems"], + "summary": "creates a coversheet and prepends it to the existing s3 document", + "description": "creates a coversheet and prepends it to the existing s3 document.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/trialSession" - } + "description": "200 response" } }, "security": [ @@ -3857,26 +3833,29 @@ } } }, - "/trial-sessions/{trialSessionId}/set-calendar": { + "/case-documents/{caseId}/{documentId}/sign": { "parameters": [ { - "name": "trialSessionId", + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "documentId", "in": "path", "required": true, "type": "string" } ], "post": { - "tags": ["trialsessions"], - "summary": "sets the calendar for a trial session", - "description": "sets the calendar for a trial session.\n", + "tags": ["documents"], + "summary": "creates a copy of an existing document, adds a signature, and adds it to the associated case", + "description": "creates a copy of an existing document, adds a signature, and adds it to the associated case.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/trialSession" - } + "description": "200 response" } }, "security": [ @@ -3909,23 +3888,26 @@ } } }, - "/trial-sessions/{trialSessionId}/generate-notices": { + "/work-items/{workItemId}/complete": { "parameters": [ { - "name": "trialSessionId", + "name": "workItemId", "in": "path", "required": true, "type": "string" } ], - "post": { - "tags": ["trialsessions"], - "summary": "generates notices of trial and standing pretrial document for cases within a given trial session and returns a PDF for any paper service parties associated", - "description": "generates notices of trial and standing pretrial document for cases within a given trial session and returns a PDF for any paper service parties associated.\n", - "produces": ["application/pdf"], + "put": { + "tags": ["workitems"], + "summary": "marks the workitem complete", + "description": "marks the workitem complete.\n", + "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/workItem" + } } }, "security": [ @@ -3958,19 +3940,49 @@ } } }, - "/trial-sessions/{trialSessionId}/getAssociatedCases": { - "parameters": [ - { - "name": "trialSessionId", - "in": "path", - "required": true, - "type": "string" - } - ], + "/trial-sessions": { "get": { "tags": ["trialsessions"], - "summary": "gets the cases associated with a trial session", - "description": "gets the cases associated with a trial session.\n", + "summary": "gets all trial sessions", + "description": "gets all trial sessions.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/trialSession" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "post": { + "tags": ["trialsessions"], + "summary": "creates a new trial session", + "description": "creates a new trial session.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/trialSession" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "put": { + "tags": ["trialsessions"], + "summary": "updates a trial session", + "description": "updates a trial session.\n", "produces": ["application/json"], "responses": { "200": { @@ -4010,31 +4022,25 @@ } } }, - "/trial-sessions/{trialSessionId}/remove-case/{caseId}": { + "/trial-sessions/{trialSessionId}": { "parameters": [ { "name": "trialSessionId", "in": "path", "required": true, "type": "string" - }, - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" } ], - "put": { + "get": { "tags": ["trialsessions"], - "summary": "removes a case from a trial session", - "description": "removes a case from a trial session.\n", + "summary": "gets a trial session", + "description": "gets a trial session.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/case" + "$ref": "#/definitions/trialSession" } } }, @@ -4068,31 +4074,25 @@ } } }, - "/trial-sessions/{trialSessionId}/cases/{caseId}": { + "/trial-sessions/{trialSessionId}/set-swing-session": { "parameters": [ { "name": "trialSessionId", "in": "path", "required": true, "type": "string" - }, - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" } ], "post": { "tags": ["trialsessions"], - "summary": "adds a case to a trial session", - "description": "adds a case to a trial session.\n", + "summary": "sets a trial session as a swing session", + "description": "sets a trial session as a swing session.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/case" + "$ref": "#/definitions/trialSession" } } }, @@ -4126,14 +4126,26 @@ } } }, - "/reports/planning-report": { - "post": { - "summary": "create a pdf of the trial session planning report", - "description": "create a pdf of the trial session planning report.\n", - "produces": ["application/pdf"], + "/trial-sessions/{trialSessionId}/eligible-cases": { + "parameters": [ + { + "name": "trialSessionId", + "in": "path", + "required": true, + "type": "string" + } + ], + "get": { + "tags": ["trialsessions"], + "summary": "gets eligible cases for a trial session", + "description": "gets eligible cases for a trial session.\n", + "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/trialSession" + } } }, "security": [ @@ -4166,37 +4178,26 @@ } } }, - "/case-documents/{caseId}/court-issued-docket-entry": { + "/trial-sessions/{trialSessionId}/set-calendar": { "parameters": [ { - "name": "caseId", + "name": "trialSessionId", "in": "path", "required": true, "type": "string" } ], "post": { - "summary": "creates a docket entry for a court-issued document on the case", - "description": "creates a docket entry for a court-issued document on the case.\n", - "produces": ["application/pdf"], - "responses": { - "200": { - "description": "200 response" - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "put": { - "summary": "updates a docket entry for a court-issued document on the case", - "description": "updates a docket entry for a court-issued document on the case.\n", - "produces": ["application/pdf"], + "tags": ["trialsessions"], + "summary": "sets the calendar for a trial session", + "description": "sets the calendar for a trial session.\n", + "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/trialSession" + } } }, "security": [ @@ -4229,25 +4230,25 @@ } } }, - "/case-documents/{caseId}/court-issued-order": { + "/trial-sessions/{trialSessionId}/generate-notices": { "parameters": [ { - "name": "caseId", + "name": "trialSessionId", "in": "path", "required": true, "type": "string" } ], "post": { - "summary": "files a court issued order on the case", - "description": "files a court issued order on the case.\n", + "tags": ["trialsessions"], + "summary": "generates notices of trial and standing pretrial document for cases within a given trial session and returns a PDF for any paper service parties associated", + "description": "generates notices of trial and standing pretrial document for cases within a given trial session and returns a PDF for any paper service parties associated.\n", "produces": ["application/pdf"], "responses": { "200": { "description": "200 response" } }, - "security": [ { "CognitoUserPool": [] @@ -4278,67 +4279,28 @@ } } }, - "/case-documents/{caseId}/court-issued-orders/{documentId}": { + "/trial-sessions/{trialSessionId}/getAssociatedCases": { "parameters": [ { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "documentId", + "name": "trialSessionId", "in": "path", "required": true, "type": "string" - }, - { - "in": "body", - "name": "draft order", - "description": "the draft order information", - "schema": { - "type": "object", - "properties": { - "documentMetadata": { - "type": "object", - "properties": { - "caseId": { - "type": "string" - }, - "docketNumber": { - "type": "string" - }, - "documentTitle": { - "type": "string" - }, - "documentType": { - "type": "string" - }, - "draftState": { - "type": "object" - }, - "eventCode": { - "type": "string" - }, - "richText": { - "type": "string" - } - } - } - } - } } ], - "put": { - "summary": "updates the draft court issued order", - "description": "updates the draft court issued order.\n", - "produces": ["application/pdf"], + "get": { + "tags": ["trialsessions"], + "summary": "gets the cases associated with a trial session", + "description": "gets the cases associated with a trial session.\n", + "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/trialSession" + } } }, - "security": [ { "CognitoUserPool": [] @@ -4369,14 +4331,32 @@ } } }, - "/documents/filing-receipt-pdf": { - "post": { - "summary": "create a pdf receipt for filing a document or documents", - "description": "create a pdf receipt for filing a document or documents.\n", - "produces": ["application/pdf"], + "/trial-sessions/{trialSessionId}/remove-case/{caseId}": { + "parameters": [ + { + "name": "trialSessionId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "put": { + "tags": ["trialsessions"], + "summary": "removes a case from a trial session", + "description": "removes a case from a trial session.\n", + "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" + } } }, "security": [ @@ -4409,17 +4389,31 @@ } } }, - "/case-deadlines": { - "get": { - "tags": ["caseDeadlines"], - "summary": "get all case deadlines", - "description": "get all case deadlines.\n", + "/trial-sessions/{trialSessionId}/cases/{caseId}": { + "parameters": [ + { + "name": "trialSessionId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "post": { + "tags": ["trialsessions"], + "summary": "adds a case to a trial session", + "description": "adds a case to a trial session.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/caseDeadline" + "$ref": "#/definitions/case" } } }, @@ -4453,25 +4447,669 @@ } } }, - "/case-deadlines/{caseId}": { - "parameters": [ - { - "name": "caseId", + "/reports/planning-report": { + "post": { + "summary": "create a pdf of the trial session planning report", + "description": "create a pdf of the trial session planning report.\n", + "produces": ["application/pdf"], + "responses": { + "200": { + "description": "200 response" + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-documents/{caseId}/court-issued-docket-entry": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "post": { + "summary": "creates a docket entry for a court-issued document on the case", + "description": "creates a docket entry for a court-issued document on the case.\n", + "produces": ["application/pdf"], + "responses": { + "200": { + "description": "200 response" + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "put": { + "summary": "updates a docket entry for a court-issued document on the case", + "description": "updates a docket entry for a court-issued document on the case.\n", + "produces": ["application/pdf"], + "responses": { + "200": { + "description": "200 response" + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-documents/{caseId}/court-issued-order": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "post": { + "summary": "files a court issued order on the case", + "description": "files a court issued order on the case.\n", + "produces": ["application/pdf"], + "responses": { + "200": { + "description": "200 response" + } + }, + + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-documents/{caseId}/court-issued-orders/{documentId}": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "documentId", + "in": "path", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "draft order", + "description": "the draft order information", + "schema": { + "type": "object", + "properties": { + "documentMetadata": { + "type": "object", + "properties": { + "caseId": { + "type": "string" + }, + "docketNumber": { + "type": "string" + }, + "documentTitle": { + "type": "string" + }, + "documentType": { + "type": "string" + }, + "draftState": { + "type": "object" + }, + "eventCode": { + "type": "string" + }, + "richText": { + "type": "string" + } + } + } + } + } + } + ], + "put": { + "summary": "updates the draft court issued order", + "description": "updates the draft court issued order.\n", + "produces": ["application/pdf"], + "responses": { + "200": { + "description": "200 response" + } + }, + + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/documents/filing-receipt-pdf": { + "post": { + "summary": "create a pdf receipt for filing a document or documents", + "description": "create a pdf receipt for filing a document or documents.\n", + "produces": ["application/pdf"], + "responses": { + "200": { + "description": "200 response" + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-deadlines": { + "get": { + "tags": ["caseDeadlines"], + "summary": "get all case deadlines", + "description": "get all case deadlines.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/caseDeadline" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-deadlines/{caseId}": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "get": { + "tags": ["caseDeadlines"], + "summary": "gets deadlines for a case", + "description": "gets deadlines for a case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/caseDeadline" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "post": { + "tags": ["caseDeadlines"], + "summary": "create and associate a deadline to a case", + "description": "create and associate a deadline to a case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/caseDeadline" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-deadlines/{caseId}/{caseDeadlineId}": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "caseDeadlineId", + "in": "path", + "required": true, + "type": "string" + } + ], + "put": { + "tags": ["caseDeadlines"], + "summary": "updates a case deadline", + "description": "updates a case deadline.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/trialSession" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "delete": { + "tags": ["caseDeadlines"], + "summary": "delete a case deadline", + "description": "delete a case deadline.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response" + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-parties/{caseId}/petition-details": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "put": { + "tags": ["cases"], + "summary": "updates the petition details on the case", + "description": "updates the petition details on the case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/user" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-parties/{caseId}/petitioner-info": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "put": { + "tags": ["cases"], + "summary": "updates the petitioner information on the case", + "description": "updates the petitioner information on the case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/user" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-meta/{caseId}/qc-complete": { + "parameters": [ + { + "name": "caseId", "in": "path", "required": true, "type": "string" + }, + { + "in": "body", + "name": "trial session info", + "description": "the trial session info needed to identify and update a trial session", + "schema": { + "type": "object", + "properties": { + "qcCompleteForTrial": { + "type": "boolean" + }, + "trialSessionId": { + "type": "string" + } + } + } } ], - "get": { - "tags": ["caseDeadlines"], - "summary": "gets deadlines for a case", - "description": "gets deadlines for a case.\n", + "put": { + "tags": ["cases"], + "summary": "updates the case to be marked as QCed", + "description": "updates the case to be marked as QCed.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/caseDeadline" + "$ref": "#/definitions/user" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-meta/{caseId}/seal": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], + "put": { + "tags": ["cases"], + "summary": "updates the case as sealed", + "description": "updates the case as sealed.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/case" } } }, @@ -4481,16 +5119,49 @@ } ] }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/case-parties/{caseId}/associate-private-practitioner": { + "parameters": [ + { + "name": "caseId", + "in": "path", + "required": true, + "type": "string" + } + ], "post": { - "tags": ["caseDeadlines"], - "summary": "create and associate a deadline to a case", - "description": "create and associate a deadline to a case.\n", + "tags": ["cases"], + "summary": "associates a practitioner with a case", + "description": "associates a practitioner with a case.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/caseDeadline" + "$ref": "#/definitions/user" } } }, @@ -4524,31 +5195,77 @@ } } }, - "/case-deadlines/{caseId}/{caseDeadlineId}": { + "/case-parties/{caseId}/associate-respondent": { "parameters": [ { "name": "caseId", "in": "path", "required": true, "type": "string" + } + ], + "post": { + "tags": ["cases"], + "summary": "associates a respondent with a case", + "description": "associates a respondent with a case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/user" + } + } }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "options": { + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "type": "string" + }, + "Access-Control-Allow-Methods": { + "type": "string" + }, + "Access-Control-Allow-Credentials": { + "type": "string" + }, + "Access-Control-Allow-Headers": { + "type": "string" + } + } + } + } + } + }, + "/trial-sessions/{trialSessionId}/working-copy": { + "parameters": [ { - "name": "caseDeadlineId", + "name": "trialSessionId", "in": "path", "required": true, "type": "string" } ], - "put": { - "tags": ["caseDeadlines"], - "summary": "updates a case deadline", - "description": "updates a case deadline.\n", + "get": { + "tags": ["trialsessions"], + "summary": "gets the trial session working copy for a trial session for the logged in user", + "description": "gets the trial session working copy for a trial session for the logged in user.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/trialSession" + "$ref": "#/definitions/trialSessionWorkingCopy" } } }, @@ -4558,14 +5275,17 @@ } ] }, - "delete": { - "tags": ["caseDeadlines"], - "summary": "delete a case deadline", - "description": "delete a case deadline.\n", + "put": { + "tags": ["trialsessions"], + "summary": "updates the trial session working copy for a trial session for the logged in user", + "description": "updates the trial session working copy for a trial session for the logged in user.\n", "produces": ["application/json"], "responses": { "200": { - "description": "200 response" + "description": "200 response", + "schema": { + "$ref": "#/definitions/trialSessionWorkingCopy" + } } }, "security": [ @@ -4598,7 +5318,7 @@ } } }, - "/case-parties/{caseId}/petition-details": { + "/case-notes/{caseId}/user-notes": { "parameters": [ { "name": "caseId", @@ -4607,16 +5327,73 @@ "type": "string" } ], + "get": { + "tags": ["caseNotes"], + "summary": "gets the logged in user's note for a case", + "description": "gets the logged in user's note for a case.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/userNote" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "post": { + "tags": ["caseNotes"], + "summary": "create and associate a note to a case for the logged in user", + "description": "create and associate a note to a case for the logged in user.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/userNote" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, "put": { - "tags": ["cases"], - "summary": "updates the petition details on the case", - "description": "updates the petition details on the case.\n", + "tags": ["caseNotes"], + "summary": "update a user's note for the logged in user", + "description": "update a user's note for the logged in user.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/userNote" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "delete": { + "tags": ["caseNotes"], + "summary": "delete the user's note for the logged in user", + "description": "delete the user's note for the logged in user.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/userNote" } } }, @@ -4650,7 +5427,7 @@ } } }, - "/case-parties/{caseId}/petitioner-info": { + "/case-notes/{caseId}": { "parameters": [ { "name": "caseId", @@ -4660,15 +5437,34 @@ } ], "put": { - "tags": ["cases"], - "summary": "updates the petitioner information on the case", - "description": "updates the petitioner information on the case.\n", + "tags": ["caseNotes"], + "summary": "update a case procedural note", + "description": "update a case procedural note.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/caseNote" + } + } + }, + "security": [ + { + "CognitoUserPool": [] + } + ] + }, + "delete": { + "tags": ["caseNotes"], + "summary": "delete the case procedural note", + "description": "delete the case procedural note.\n", + "produces": ["application/json"], + "responses": { + "200": { + "description": "200 response", + "schema": { + "$ref": "#/definitions/caseNote" } } }, @@ -4702,41 +5498,17 @@ } } }, - "/case-meta/{caseId}/qc-complete": { - "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "trial session info", - "description": "the trial session info needed to identify and update a trial session", - "schema": { - "type": "object", - "properties": { - "qcCompleteForTrial": { - "type": "boolean" - }, - "trialSessionId": { - "type": "string" - } - } - } - } - ], - "put": { - "tags": ["cases"], - "summary": "updates the case to be marked as QCed", - "description": "updates the case to be marked as QCed.\n", + "/messages": { + "post": { + "tags": ["messages"], + "summary": "create a case message", + "description": "Create a message.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/caseMessage" } } }, @@ -4770,25 +5542,25 @@ } } }, - "/case-meta/{caseId}/seal": { + "/messages/{messageId}": { "parameters": [ { - "name": "caseId", + "name": "messageId", "in": "path", "required": true, "type": "string" } ], - "put": { - "tags": ["cases"], - "summary": "updates the case as sealed", - "description": "updates the case as sealed.\n", + "get": { + "tags": ["messages"], + "summary": "gets the message for the specific id", + "description": "Gets the message for the specific id.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/case" + "$ref": "#/definitions/caseMessage" } } }, @@ -4822,25 +5594,25 @@ } } }, - "/case-parties/{caseId}/associate-private-practitioner": { + "/messages/inbox/{userId}": { "parameters": [ { - "name": "caseId", + "name": "userId", "in": "path", "required": true, "type": "string" } ], - "post": { - "tags": ["cases"], - "summary": "associates a practitioner with a case", - "description": "associates a practitioner with a case.\n", + "get": { + "tags": ["messages"], + "summary": "gets the inbox messages for the specific user id", + "description": "Gets the inbox messages for the specific user id.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/caseMessage" } } }, @@ -4874,25 +5646,25 @@ } } }, - "/case-parties/{caseId}/associate-respondent": { + "/messages/inbox/section/{section}": { "parameters": [ { - "name": "caseId", + "name": "section", "in": "path", "required": true, "type": "string" } ], - "post": { - "tags": ["cases"], - "summary": "associates a respondent with a case", - "description": "associates a respondent with a case.\n", + "get": { + "tags": ["messages"], + "summary": "gets the inbox messages for the specific section", + "description": "Gets the inbox messages for the specific user section.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/user" + "$ref": "#/definitions/caseMessage" } } }, @@ -4926,44 +5698,25 @@ } } }, - "/trial-sessions/{trialSessionId}/working-copy": { + "/messages/outbox/{userId}": { "parameters": [ { - "name": "trialSessionId", + "name": "userId", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["trialsessions"], - "summary": "gets the trial session working copy for a trial session for the logged in user", - "description": "gets the trial session working copy for a trial session for the logged in user.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/trialSessionWorkingCopy" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "put": { - "tags": ["trialsessions"], - "summary": "updates the trial session working copy for a trial session for the logged in user", - "description": "updates the trial session working copy for a trial session for the logged in user.\n", + "tags": ["messages"], + "summary": "gets the outbox messages for the specific user id", + "description": "Gets the outbox messages for the specific user id.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/trialSessionWorkingCopy" + "$ref": "#/definitions/caseMessage" } } }, @@ -4997,82 +5750,25 @@ } } }, - "/case-notes/{caseId}/user-notes": { + "/messages/outbox/section/{section}": { "parameters": [ { - "name": "caseId", + "name": "section", "in": "path", "required": true, "type": "string" } ], "get": { - "tags": ["caseNotes"], - "summary": "gets the logged in user's note for a case", - "description": "gets the logged in user's note for a case.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/userNote" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "post": { - "tags": ["caseNotes"], - "summary": "create and associate a note to a case for the logged in user", - "description": "create and associate a note to a case for the logged in user.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/userNote" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "put": { - "tags": ["caseNotes"], - "summary": "update a user's note for the logged in user", - "description": "update a user's note for the logged in user.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/userNote" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "delete": { - "tags": ["caseNotes"], - "summary": "delete the user's note for the logged in user", - "description": "delete the user's note for the logged in user.\n", + "tags": ["messages"], + "summary": "gets the outbox messages for the specific section", + "description": "Gets the outbox messages for the specific user section.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/userNote" + "$ref": "#/definitions/caseMessage" } } }, @@ -5106,44 +5802,17 @@ } } }, - "/case-notes/{caseId}": { - "parameters": [ - { - "name": "caseId", - "in": "path", - "required": true, - "type": "string" - } - ], - "put": { - "tags": ["caseNotes"], - "summary": "update a case procedural note", - "description": "update a case procedural note.\n", - "produces": ["application/json"], - "responses": { - "200": { - "description": "200 response", - "schema": { - "$ref": "#/definitions/caseNote" - } - } - }, - "security": [ - { - "CognitoUserPool": [] - } - ] - }, - "delete": { - "tags": ["caseNotes"], - "summary": "delete the case procedural note", - "description": "delete the case procedural note.\n", + "/public-api/todays-opinions": { + "get": { + "tags": ["case-documents"], + "summary": "returns the opinions created for the current date", + "description": "Returns the opinions created for the current date.\n", "produces": ["application/json"], "responses": { "200": { "description": "200 response", "schema": { - "$ref": "#/definitions/caseNote" + "$ref": "#/definitions/document" } } }, @@ -5541,6 +6210,72 @@ } }, "description": "a case procedural note" + }, + "caseMessage": { + "type": "object", + "properties": { + "caseId": { + "type": "string", + "description": "id of the associated case" + }, + "caseStatus": { + "type": "string", + "description": "status of the associated case" + }, + "createdAt": { + "type": "string", + "description": "message created time" + }, + "docketNumber": { + "type": "string", + "description": "docket number of the associated case" + }, + "docketNumberWithSuffix": { + "type": "string", + "description": "docket number of the associated case with its suffix" + }, + "entityName": { + "type": "string", + "description": "the name of the entity - CaseMessage" + }, + "from": { + "type": "string", + "description": "the name of the user who sent the message" + }, + "fromSection": { + "type": "string", + "description": "the section of the user who sent the message" + }, + "fromUserId": { + "type": "string", + "description": "the ID of the user who sent the message" + }, + "message": { + "type": "string", + "description": "the message text" + }, + "messageId": { + "type": "string", + "description": "the unique ID generated by the system to represent the message" + }, + "subject": { + "type": "string", + "description": "the subject line of the message" + }, + "to": { + "type": "string", + "description": "the name of the user who is the recipient of the message" + }, + "toSection": { + "type": "string", + "description": "the section of the user who is the recipient of the message" + }, + "toUserId": { + "type": "string", + "description": "the ID of the user who is the recipient of the message" + } + }, + "description": "a case deadline" } } } diff --git a/web-api/terraform/main/variables.tf b/web-api/terraform/main/variables.tf index a28cc922b86..5a5105a68c0 100644 --- a/web-api/terraform/main/variables.tf +++ b/web-api/terraform/main/variables.tf @@ -25,4 +25,5 @@ variable "es_instance_count" { variable "honeybadger_key" { type = "string" -} \ No newline at end of file + default = "default_key" +} diff --git a/web-api/terraform/template/cognito.tf b/web-api/terraform/template/cognito.tf index 787ec26d9d7..feb694918ac 100644 --- a/web-api/terraform/template/cognito.tf +++ b/web-api/terraform/template/cognito.tf @@ -106,11 +106,6 @@ resource "aws_cognito_user_pool" "irs_pool" { enabled = true } - device_configuration { - challenge_required_on_new_device = true - device_only_remembered_on_user_prompt = false - } - auto_verified_attributes = ["email"] username_attributes = ["email"] diff --git a/web-api/verify-authorizers.js b/web-api/verify-authorizers.js index dba9183c883..53649392e78 100755 --- a/web-api/verify-authorizers.js +++ b/web-api/verify-authorizers.js @@ -11,7 +11,9 @@ axios.interceptors.request.use(function (config) { const main = async () => { const serverlessYmlFiles = process.argv .slice(2) - .filter(path => !path.includes('public-api')); + .filter( + path => !path.includes('public-api') && path.includes('serverless'), + ); const serverlessConfigs = serverlessYmlFiles.map(path => yaml.safeLoad(fs.readFileSync(path, 'utf8')), diff --git a/web-client/integration-tests-public/journey/unauthedUserSearchesByDocketNumber.js b/web-client/integration-tests-public/journey/unauthedUserSearchesByDocketNumber.js index 5c1d847e419..5fcdbc1d85d 100644 --- a/web-client/integration-tests-public/journey/unauthedUserSearchesByDocketNumber.js +++ b/web-client/integration-tests-public/journey/unauthedUserSearchesByDocketNumber.js @@ -11,7 +11,7 @@ export const unauthedUserSearchesByDocketNumber = (test, params) => { key: 'docketNumber', value: '123-xx', }); - await test.runSequence('submitCaseDocketNumberSearchSequence', {}); + await test.runSequence('submitPublicCaseDocketNumberSearchSequence', {}); searchResults = test.getState('searchResults'); expect(searchResults).toEqual([]); expect(test.currentRouteUrl.indexOf('/case-detail')).toEqual(-1); @@ -29,7 +29,7 @@ export const unauthedUserSearchesByDocketNumber = (test, params) => { key: 'docketNumber', value: queryParams.docketNumber, }); - await test.runSequence('submitCaseDocketNumberSearchSequence', {}); + await test.runSequence('submitPublicCaseDocketNumberSearchSequence', {}); searchResults = test.getState('searchResults'); expect(test.getState('caseId')).toEqual(params.docketNumber); expect(test.currentRouteUrl.indexOf('/case-detail')).toEqual(0); diff --git a/web-client/integration-tests-public/journey/unauthedUserSearchesForSealedCasesByDocketNumber.js b/web-client/integration-tests-public/journey/unauthedUserSearchesForSealedCasesByDocketNumber.js new file mode 100644 index 00000000000..25633ed0820 --- /dev/null +++ b/web-client/integration-tests-public/journey/unauthedUserSearchesForSealedCasesByDocketNumber.js @@ -0,0 +1,17 @@ +export const unauthedUserSearchesForSealedCasesByDocketNumber = test => { + return it('Search for a sealed case by docket number', async () => { + test.currentRouteUrl = ''; + test.setState('caseSearchByDocketNumber', {}); + + await test.runSequence('updateAdvancedSearchFormValueSequence', { + formType: 'caseSearchByDocketNumber', + key: 'docketNumber', + value: test.docketNumber, + }); + + await test.runSequence('submitPublicCaseDocketNumberSearchSequence'); + + expect(test.getState('searchResults')).toEqual([]); + expect(test.currentRouteUrl.indexOf('/case-detail')).toEqual(-1); + }); +}; diff --git a/web-client/integration-tests-public/journey/unauthedUserViewsCaseDetail.js b/web-client/integration-tests-public/journey/unauthedUserViewsCaseDetail.js index cd831e309ec..23cb7268966 100644 --- a/web-client/integration-tests-public/journey/unauthedUserViewsCaseDetail.js +++ b/web-client/integration-tests-public/journey/unauthedUserViewsCaseDetail.js @@ -77,7 +77,7 @@ export const unauthedUserViewsCaseDetail = test => { documentType: 'Statement of Taxpayer Identification', }), expect.objectContaining({ - documentType: 'OD - Order of Dismissal Entered,', + documentType: 'OD - Order of Dismissal Entered', }), expect.objectContaining({ documentType: 'TRAN - Transcript' }), ]), diff --git a/web-client/integration-tests-public/journey/unauthedUserViewsPrintableDocketRecord.js b/web-client/integration-tests-public/journey/unauthedUserViewsPrintableDocketRecord.js index 771f4b8733c..8ef54b82f76 100644 --- a/web-client/integration-tests-public/journey/unauthedUserViewsPrintableDocketRecord.js +++ b/web-client/integration-tests-public/journey/unauthedUserViewsPrintableDocketRecord.js @@ -3,6 +3,6 @@ export const unauthedUserViewsPrintableDocketRecord = test => { await test.runSequence('gotoPublicPrintableDocketRecordSequence', { docketNumber: test.docketNumber, }); - expect(test.getState('pdfPreviewUrl')).toEqual('abc'); + expect(test.getState('pdfPreviewUrl')).toBeDefined(); }); }; diff --git a/web-client/integration-tests-public/journey/unauthedUserViewsTodaysOpinions.js b/web-client/integration-tests-public/journey/unauthedUserViewsTodaysOpinions.js new file mode 100644 index 00000000000..9c73d30a400 --- /dev/null +++ b/web-client/integration-tests-public/journey/unauthedUserViewsTodaysOpinions.js @@ -0,0 +1,18 @@ +import { refreshElasticsearchIndex } from '../../integration-tests/helpers'; + +export const unauthedUserViewsTodaysOpinions = test => { + return it('should view todays opinions', async () => { + await refreshElasticsearchIndex(); + + await test.runSequence('gotoTodaysOpinionsSequence', {}); + + expect(test.getState('todaysOpinions')).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + documentType: 'TCOP - T.C. Opinion', + numberOfPages: 1, + }), + ]), + ); + }); +}; diff --git a/web-client/integration-tests-public/unauthedUserSearchesForCase.test.js b/web-client/integration-tests-public/unauthedUserSearchesForCase.test.js index 2f753ee214b..5a23172d4b7 100644 --- a/web-client/integration-tests-public/unauthedUserSearchesForCase.test.js +++ b/web-client/integration-tests-public/unauthedUserSearchesForCase.test.js @@ -15,7 +15,7 @@ import { petitionerCreatesNewCase } from '../integration-tests/journey/petitione import { docketClerkAddsDocketEntryFromOrderOfDismissal } from '../integration-tests/journey/docketClerkAddsDocketEntryFromOrderOfDismissal'; import { docketClerkAddsTranscriptDocketEntryFromOrder } from '../integration-tests/journey/docketClerkAddsTranscriptDocketEntryFromOrder'; import { docketClerkCreatesAnOrder } from '../integration-tests/journey/docketClerkCreatesAnOrder'; -import { docketClerkServesOrder } from '../integration-tests/journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from '../integration-tests/journey/docketClerkServesDocument'; // Public User import { unauthedUserNavigatesToPublicSite } from './journey/unauthedUserNavigatesToPublicSite'; @@ -29,6 +29,7 @@ const test = setupTest({ loadPDFForSigningInteractor: () => Promise.resolve(null), }, }); + const testClient = setupTestClient({ useCases: { loadPDFForSigningInteractor: () => Promise.resolve(null), @@ -62,7 +63,7 @@ describe('Docket clerk creates and serves an order (should be viewable to the pu expectedDocumentType: 'Order of Dismissal', }); docketClerkAddsDocketEntryFromOrderOfDismissal(testClient, 1); - docketClerkServesOrder(testClient, 1); + docketClerkServesDocument(testClient, 1); }); describe('Docket clerk creates and serves a transcript (should not be viewable to the public)', () => { @@ -77,7 +78,7 @@ describe('Docket clerk creates and serves a transcript (should not be viewable t month: '01', year: '2019', }); - docketClerkServesOrder(testClient, 2); + docketClerkServesDocument(testClient, 2); }); describe('Unauthed user searches for a case and views a case detail page', () => { diff --git a/web-client/integration-tests-public/unauthedUserSearchesForOrder.test.js b/web-client/integration-tests-public/unauthedUserSearchesForOrder.test.js index 64b9bd6e261..6d3fde04268 100644 --- a/web-client/integration-tests-public/unauthedUserSearchesForOrder.test.js +++ b/web-client/integration-tests-public/unauthedUserSearchesForOrder.test.js @@ -1,9 +1,9 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { docketClerkAddsDocketEntryFromOrder } from '../integration-tests/journey/docketClerkAddsDocketEntryFromOrder'; import { docketClerkAddsDocketEntryFromOrderOfDismissal } from '../integration-tests/journey/docketClerkAddsDocketEntryFromOrderOfDismissal'; import { docketClerkCreatesAnOrder } from '../integration-tests/journey/docketClerkCreatesAnOrder'; import { docketClerkSealsCase } from '../integration-tests/journey/docketClerkSealsCase'; -import { docketClerkServesOrder } from '../integration-tests/journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from '../integration-tests/journey/docketClerkServesDocument'; import { loginAs, setupTest as setupTestClient, @@ -41,7 +41,7 @@ describe('Petitioner creates case', () => { postalCode: '77546', state: 'CT', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; @@ -58,7 +58,7 @@ describe('Docket clerk creates orders to search for', () => { signedAtFormatted: '01/02/2020', }); docketClerkAddsDocketEntryFromOrder(testClient, 0); - docketClerkServesOrder(testClient, 0); + docketClerkServesDocument(testClient, 0); docketClerkCreatesAnOrder(testClient, { documentTitle: 'Order of Dismissal', @@ -66,7 +66,7 @@ describe('Docket clerk creates orders to search for', () => { expectedDocumentType: 'Order of Dismissal', }); docketClerkAddsDocketEntryFromOrderOfDismissal(testClient, 1); - docketClerkServesOrder(testClient, 1); + docketClerkServesDocument(testClient, 1); docketClerkCreatesAnOrder(testClient, { documentTitle: 'Order of Dismissal', diff --git a/web-client/integration-tests-public/unauthedUserSearchesForSealedCase.test.js b/web-client/integration-tests-public/unauthedUserSearchesForSealedCase.test.js index d6089f871a0..486117d7334 100644 --- a/web-client/integration-tests-public/unauthedUserSearchesForSealedCase.test.js +++ b/web-client/integration-tests-public/unauthedUserSearchesForSealedCase.test.js @@ -1,17 +1,14 @@ -import { setupTest } from './helpers'; - +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; +import { docketClerkSealsCase } from '../integration-tests/journey/docketClerkSealsCase'; import { loginAs, setupTest as setupTestClient, uploadPetition, } from '../integration-tests/helpers'; - -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; - -// Public User -import { docketClerkSealsCase } from '../integration-tests/journey/docketClerkSealsCase'; +import { setupTest } from './helpers'; import { unauthedUserNavigatesToPublicSite } from './journey/unauthedUserNavigatesToPublicSite'; import { unauthedUserSearchesForSealedCaseByName } from './journey/unauthedUserSearchesForSealedCaseByName'; +import { unauthedUserSearchesForSealedCasesByDocketNumber } from './journey/unauthedUserSearchesForSealedCasesByDocketNumber'; import { unauthedUserViewsCaseDetailForSealedCase } from './journey/unauthedUserViewsCaseDetailForSealedCase'; import { unauthedUserViewsPrintableDocketRecord } from './journey/unauthedUserViewsPrintableDocketRecord'; @@ -36,7 +33,7 @@ describe('Petitioner creates cases to search for', () => { postalCode: '77546', state: 'CT', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; @@ -60,3 +57,8 @@ describe('Unauthed user searches for a sealed case by name', () => { unauthedUserSearchesForSealedCaseByName(test); unauthedUserViewsPrintableDocketRecord(test); }); + +describe('Unauthed user searches for a sealed case and does not route to the case detail page', () => { + unauthedUserNavigatesToPublicSite(test); + unauthedUserSearchesForSealedCasesByDocketNumber(test); +}); diff --git a/web-client/integration-tests-public/unauthedUserViewsTodaysOpinions.test.js b/web-client/integration-tests-public/unauthedUserViewsTodaysOpinions.test.js new file mode 100644 index 00000000000..1f56f402d10 --- /dev/null +++ b/web-client/integration-tests-public/unauthedUserViewsTodaysOpinions.test.js @@ -0,0 +1,46 @@ +import { docketClerkAddsDocketEntryFromOrder } from '../integration-tests/journey/docketClerkAddsDocketEntryFromOrder'; +import { docketClerkConvertsAnOrderToAnOpinion } from '../integration-tests/journey/docketClerkConvertsAnOrderToAnOpinion'; +import { docketClerkCreatesAnOrder } from '../integration-tests/journey/docketClerkCreatesAnOrder'; +import { docketClerkServesDocument } from '../integration-tests/journey/docketClerkServesDocument'; +import { docketClerkViewsDraftOrder } from '../integration-tests/journey/docketClerkViewsDraftOrder'; + +import { + loginAs, + setupTest as setupTestClient, + uploadPetition, +} from '../integration-tests/helpers'; +import { setupTest } from './helpers'; +import { unauthedUserViewsTodaysOpinions } from './journey/unauthedUserViewsTodaysOpinions'; + +const test = setupTest(); +const testClient = setupTestClient({ + useCases: { + loadPDFForSigningInteractor: () => Promise.resolve(null), + }, +}); + +testClient.draftOrders = []; + +describe('Unauthed user views todays opinions', () => { + loginAs(testClient, 'petitioner'); + it('Create test case to add an opinion to', async () => { + const caseDetail = await uploadPetition(testClient); + expect(caseDetail.docketNumber).toBeDefined(); + testClient.docketNumber = caseDetail.docketNumber; + }); + + // the next few tests create an order document, then edit it to convert + // it to an opinion type document, and then serve that opinion in order for it to show up in todays opinions + loginAs(testClient, 'docketclerk'); + docketClerkCreatesAnOrder(testClient, { + documentTitle: 'Order to do something', + eventCode: 'O', + expectedDocumentType: 'Order', + }); + docketClerkViewsDraftOrder(testClient, 0); + docketClerkAddsDocketEntryFromOrder(testClient, 0); + docketClerkConvertsAnOrderToAnOpinion(testClient, 0); + docketClerkServesDocument(testClient, 0); + + unauthedUserViewsTodaysOpinions(test); +}); diff --git a/web-client/integration-tests/admissionsClerkPractitionerJourney.test.js b/web-client/integration-tests/admissionsClerkPractitionerJourney.test.js index d0abfb701f2..9da92ae4bd2 100644 --- a/web-client/integration-tests/admissionsClerkPractitionerJourney.test.js +++ b/web-client/integration-tests/admissionsClerkPractitionerJourney.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { admissionsClerkAddsNewPractitioner } from './journey/admissionsClerkAddsNewPractitioner'; import { admissionsClerkEditsPractitionerInfo } from './journey/admissionsClerkEditsPractitionerInfo'; import { admissionsClerkSearchesForPractitionerByBarNumber } from './journey/admissionsClerkSearchesForPractitionerByBarNumber'; @@ -32,7 +32,7 @@ describe('admissions clerk practitioner journey', () => { postalCode: '77546', state: 'AZ', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; diff --git a/web-client/integration-tests/caseJourney.test.js b/web-client/integration-tests/caseJourney.test.js index b826eca45e1..f6af253a763 100644 --- a/web-client/integration-tests/caseJourney.test.js +++ b/web-client/integration-tests/caseJourney.test.js @@ -1,6 +1,5 @@ -import { adcMarksStipulatedWorkItemAsCompleted } from './journey/adcMarksStipulatedWorkItemAsCompleted'; +import { adcMarksWorkItemCompleteAndViewsCaseDetailAfterComplete } from './journey/adcMarksWorkItemCompleteAndViewsCaseDetailAfterComplete'; import { adcViewsCaseDetail } from './journey/adcViewsCaseDetail'; -import { adcViewsCaseDetailAfterComplete } from './journey/adcViewsCaseDetailAfterComplete'; import { adcViewsDocumentDetail } from './journey/adcViewsDocumentDetail'; import { adcViewsMessages } from './journey/adcViewsMessages'; import { adcViewsMessagesAfterComplete } from './journey/adcViewsMessagesAfterComplete'; @@ -104,7 +103,6 @@ describe('Case journey', () => { adcViewsMessages(test); adcViewsCaseDetail(test); adcViewsDocumentDetail(test); - adcMarksStipulatedWorkItemAsCompleted(test); - adcViewsCaseDetailAfterComplete(test); + adcMarksWorkItemCompleteAndViewsCaseDetailAfterComplete(test); adcViewsMessagesAfterComplete(test); }); diff --git a/web-client/integration-tests/correspondenceJourney.test.js b/web-client/integration-tests/correspondenceJourney.test.js new file mode 100644 index 00000000000..cbbfba96256 --- /dev/null +++ b/web-client/integration-tests/correspondenceJourney.test.js @@ -0,0 +1,30 @@ +import { docketClerkAddsCorrespondence } from './journey/docketClerkAddsCorrespondence'; +import { docketClerkDeletesCorrespondence } from './journey/docketClerkDeletesCorrespondence'; +import { docketClerkEditsCorrespondence } from './journey/docketClerkEditsCorrespondence'; +import { docketClerkNavigatesToAddCorrespondence } from './journey/docketClerkNavigatesToAddCorrespondence'; +import { docketClerkNavigatesToEditCorrespondence } from './journey/docketClerkNavigatesToEditCorrespondence'; +import { loginAs, setupTest, uploadPetition } from './helpers'; + +const test = setupTest(); +let caseDetail; +const correspondenceTitle = 'My correspondence'; + +describe('Adds correspondence to a case', () => { + beforeAll(() => { + jest.setTimeout(30000); + }); + + loginAs(test, 'petitioner'); + it('create case', async () => { + caseDetail = await uploadPetition(test); + expect(caseDetail).toBeDefined(); + test.docketNumber = caseDetail.docketNumber; + }); + + loginAs(test, 'docketclerk'); + docketClerkNavigatesToAddCorrespondence(test); + docketClerkAddsCorrespondence(test, correspondenceTitle); + docketClerkNavigatesToEditCorrespondence(test, correspondenceTitle); + docketClerkEditsCorrespondence(test); + docketClerkDeletesCorrespondence(test); +}); diff --git a/web-client/integration-tests/docketClerkAddsCourtIssuedOrderToDocketRecord.test.js b/web-client/integration-tests/docketClerkAddsCourtIssuedOrderToDocketRecord.test.js index ec13b48e9c1..442d1462555 100644 --- a/web-client/integration-tests/docketClerkAddsCourtIssuedOrderToDocketRecord.test.js +++ b/web-client/integration-tests/docketClerkAddsCourtIssuedOrderToDocketRecord.test.js @@ -5,9 +5,9 @@ import { docketClerkAddsDocketEntryFromOrder } from './journey/docketClerkAddsDo import { docketClerkAddsDocketEntryFromOrderOfDismissal } from './journey/docketClerkAddsDocketEntryFromOrderOfDismissal'; import { docketClerkAddsDocketEntryFromOrderWithDate } from './journey/docketClerkAddsDocketEntryFromOrderWithDate'; import { docketClerkCancelsAddDocketEntryFromOrder } from './journey/docketClerkCancelsAddDocketEntryFromOrder'; +import { docketClerkConvertsAnOrderToAnOpinion } from './journey/docketClerkConvertsAnOrderToAnOpinion'; import { docketClerkCreatesAnOrder } from './journey/docketClerkCreatesAnOrder'; import { docketClerkEditsDocketEntryFromOrderTypeA } from './journey/docketClerkEditsDocketEntryFromOrderTypeA'; -import { docketClerkEditsDocketEntryFromOrderTypeB } from './journey/docketClerkEditsDocketEntryFromOrderTypeB'; import { docketClerkEditsDocketEntryFromOrderTypeC } from './journey/docketClerkEditsDocketEntryFromOrderTypeC'; import { docketClerkEditsDocketEntryFromOrderTypeD } from './journey/docketClerkEditsDocketEntryFromOrderTypeD'; import { docketClerkEditsDocketEntryFromOrderTypeE } from './journey/docketClerkEditsDocketEntryFromOrderTypeE'; @@ -64,7 +64,7 @@ describe('Docket Clerk Adds Court-Issued Order to Docket Record', () => { docketClerkViewsDraftOrder(test, 0); docketClerkAddsDocketEntryFromOrder(test, 0); docketClerkEditsDocketEntryFromOrderTypeA(test, 0); - docketClerkEditsDocketEntryFromOrderTypeB(test, 0); + docketClerkConvertsAnOrderToAnOpinion(test, 0); docketClerkEditsDocketEntryFromOrderTypeC(test, 0); docketClerkEditsDocketEntryFromOrderTypeD(test, 0); docketClerkEditsDocketEntryFromOrderTypeE(test, 0); diff --git a/web-client/integration-tests/docketClerkAddsTranscriptToDocketRecord.test.js b/web-client/integration-tests/docketClerkAddsTranscriptToDocketRecord.test.js index 5f9b13bfa43..a98c673d8df 100644 --- a/web-client/integration-tests/docketClerkAddsTranscriptToDocketRecord.test.js +++ b/web-client/integration-tests/docketClerkAddsTranscriptToDocketRecord.test.js @@ -1,6 +1,6 @@ import { docketClerkAddsTranscriptDocketEntryFromOrder } from './journey/docketClerkAddsTranscriptDocketEntryFromOrder'; import { docketClerkCreatesAnOrder } from './journey/docketClerkCreatesAnOrder'; -import { docketClerkServesOrder } from './journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from './journey/docketClerkServesDocument'; import { docketClerkViewsDraftOrder } from './journey/docketClerkViewsDraftOrder'; import { fakeFile, loginAs, setupTest } from './helpers'; import { formattedCaseDetail as formattedCaseDetailComputed } from '../src/presenter/computeds/formattedCaseDetail'; @@ -44,7 +44,7 @@ describe('Docket Clerk Adds Transcript to Docket Record', () => { month: '01', year: '2019', }); - docketClerkServesOrder(test, 0); + docketClerkServesDocument(test, 0); docketClerkCreatesAnOrder(test, { documentTitle: 'Order to do something', eventCode: 'O', @@ -58,7 +58,7 @@ describe('Docket Clerk Adds Transcript to Docket Record', () => { month: today.getMonth() + 1, year: today.getFullYear(), }); - docketClerkServesOrder(test, 1); + docketClerkServesDocument(test, 1); loginAs(test, 'petitioner'); it('petitioner views transcript on docket record', async () => { diff --git a/web-client/integration-tests/docketClerkCaseInventoryReport.test.js b/web-client/integration-tests/docketClerkCaseInventoryReport.test.js index bb9a427c530..d2af90d805e 100644 --- a/web-client/integration-tests/docketClerkCaseInventoryReport.test.js +++ b/web-client/integration-tests/docketClerkCaseInventoryReport.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { docketClerkCreatesATrialSession } from './journey/docketClerkCreatesATrialSession'; import { docketClerkViewsTrialSessionList } from './journey/docketClerkViewsTrialSessionList'; import { @@ -30,7 +30,7 @@ describe('case inventory report journey', () => { //New await test.runSequence('updateScreenMetadataSequence', { key: 'status', - value: Case.STATUS_TYPES.new, + value: CASE_STATUS_TYPES.new, }); await test.runSequence('submitCaseInventoryReportModalSequence'); initialCaseInventoryCounts.new = test.getState( @@ -48,7 +48,7 @@ describe('case inventory report journey', () => { //Calendared, Judge Armen await test.runSequence('updateScreenMetadataSequence', { key: 'status', - value: Case.STATUS_TYPES.calendared, + value: CASE_STATUS_TYPES.calendared, }); await test.runSequence('submitCaseInventoryReportModalSequence'); initialCaseInventoryCounts.calendaredArmen = test.getState( @@ -120,7 +120,7 @@ describe('case inventory report journey', () => { await test.runSequence('openCaseInventoryReportModalSequence'); await test.runSequence('updateScreenMetadataSequence', { key: 'status', - value: Case.STATUS_TYPES.new, + value: CASE_STATUS_TYPES.new, }); await test.runSequence('submitCaseInventoryReportModalSequence'); let updatedCaseInventoryCount = test.getState( @@ -144,7 +144,7 @@ describe('case inventory report journey', () => { //Calendared, Judge Armen (+1 from initial) await test.runSequence('updateScreenMetadataSequence', { key: 'status', - value: Case.STATUS_TYPES.calendared, + value: CASE_STATUS_TYPES.calendared, }); await test.runSequence('submitCaseInventoryReportModalSequence'); updatedCaseInventoryCount = test.getState( diff --git a/web-client/integration-tests/docketClerkCreatesDocketEntryFromScans.test.js b/web-client/integration-tests/docketClerkCreatesDocketEntryFromScans.test.js index 41cb152849a..46d7fd7b247 100644 --- a/web-client/integration-tests/docketClerkCreatesDocketEntryFromScans.test.js +++ b/web-client/integration-tests/docketClerkCreatesDocketEntryFromScans.test.js @@ -1,80 +1,20 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; -import { CerebralTest } from 'cerebral/test'; -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; -import { JSDOM } from 'jsdom'; -import { MAX_FILE_SIZE_MB } from '../../shared/src/persistence/s3/getUploadPolicy'; -import { TrialSession } from '../../shared/src/business/entities/trialSessions/TrialSession'; -import { User } from '../../shared/src/business/entities/User'; import { addBatchesForScanning, createPDFFromScannedBatches, selectScannerSource, -} from './scanHelpers.js'; -import { applicationContext } from '../src/applicationContext'; +} from './scanHelpers'; import { docketClerkAddsDocketEntryFile } from './journey/docketClerkAddsDocketEntryFile'; import { docketClerkAddsDocketEntryWithoutFile } from './journey/docketClerkAddsDocketEntryWithoutFile'; import { docketClerkSavesDocketEntry } from './journey/docketClerkSavesDocketEntry'; import { docketClerkViewsEditDocketRecord } from './journey/docketClerkViewsEditDocketRecord'; import { docketClerkViewsQCInProgress } from './journey/docketClerkViewsQCInProgress'; import { docketClerkViewsSectionQCInProgress } from './journey/docketClerkViewsSectionQCInProgress'; -import { getScannerInterface } from '../../shared/src/persistence/dynamsoft/getScannerMockInterface'; -import { isFunction, mapValues } from 'lodash'; -import { loginAs } from './helpers'; +import { fakeFile, loginAs, setupTest } from './helpers'; import { petitionerChoosesCaseType } from './journey/petitionerChoosesCaseType'; import { petitionerChoosesProcedureType } from './journey/petitionerChoosesProcedureType'; import { petitionerCreatesNewCase } from './journey/petitionerCreatesNewCase'; -import { presenter } from '../src/presenter/presenter'; -import { withAppContextDecorator } from '../src/withAppContext'; -import FormData from 'form-data'; -let test; -global.FormData = FormData; -presenter.providers.applicationContext = Object.assign(applicationContext, { - getScanner: getScannerInterface, -}); -presenter.providers.router = { - createObjectURL: () => {}, - externalRoute: () => {}, - revokeObjectURL: () => {}, - route: async url => { - if (url === `/case-detail/${test.docketNumber}`) { - await test.runSequence('gotoCaseDetailSequence', { - docketNumber: test.docketNumber, - }); - } - - if (url === '/') { - await test.runSequence('gotoDashboardSequence'); - } - - if (url === '/file-a-petition/success') { - await test.runSequence('gotoFilePetitionSuccessSequence'); - } - }, -}; - -presenter.state = mapValues(presenter.state, value => { - if (isFunction(value)) { - return withAppContextDecorator(value, applicationContext); - } - return value; -}); - -const fakeData = - 'JVBERi0xLjEKJcKlwrHDqwoKMSAwIG9iagogIDw8IC9UeXBlIC9DYXRhbG9nCiAgICAgL1BhZ2VzIDIgMCBSCiAgPj4KZW5kb2JqCgoyIDAgb2JqCiAgPDwgL1R5cGUgL1BhZ2VzCiAgICAgL0tpZHMgWzMgMCBSXQogICAgIC9Db3VudCAxCiAgICAgL01lZGlhQm94IFswIDAgMzAwIDE0NF0KICA+PgplbmRvYmoKCjMgMCBvYmoKICA8PCAgL1R5cGUgL1BhZ2UKICAgICAgL1BhcmVudCAyIDAgUgogICAgICAvUmVzb3VyY2VzCiAgICAgICA8PCAvRm9udAogICAgICAgICAgIDw8IC9GMQogICAgICAgICAgICAgICA8PCAvVHlwZSAvRm9udAogICAgICAgICAgICAgICAgICAvU3VidHlwZSAvVHlwZTEKICAgICAgICAgICAgICAgICAgL0Jhc2VGb250IC9UaW1lcy1Sb21hbgogICAgICAgICAgICAgICA+PgogICAgICAgICAgID4+CiAgICAgICA+PgogICAgICAvQ29udGVudHMgNCAwIFIKICA+PgplbmRvYmoKCjQgMCBvYmoKICA8PCAvTGVuZ3RoIDg0ID4+CnN0cmVhbQogIEJUCiAgICAvRjEgMTggVGYKICAgIDUgODAgVGQKICAgIChDb25ncmF0aW9ucywgeW91IGZvdW5kIHRoZSBFYXN0ZXIgRWdnLikgVGoKICBFVAplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTggMDAwMDAgbiAKMDAwMDAwMDA3NyAwMDAwMCBuIAowMDAwMDAwMTc4IDAwMDAwIG4gCjAwMDAwMDA0NTcgMDAwMDAgbiAKdHJhaWxlcgogIDw8ICAvUm9vdCAxIDAgUgogICAgICAvU2l6ZSA1CiAgPj4Kc3RhcnR4cmVmCjU2NQolJUVPRgo='; - -const fakeFile = Buffer.from(fakeData, 'base64'); -fakeFile.name = 'fakeFile.pdf'; - -const dom = new JSDOM(` - - -`); - -const { window } = dom; -const { Blob, File } = window; - -test = CerebralTest(presenter); +const test = setupTest(); describe('Create Docket Entry From Scans', () => { let scannerSourceIndex = 0; @@ -82,44 +22,18 @@ describe('Create Docket Entry From Scans', () => { beforeEach(() => { jest.setTimeout(30000); - global.alert = () => null; - global.URL = { - createObjectURL: () => { - return fakeData; - }, - revokeObjectURL: () => null, - }; - global.window = { - URL: global.URL, - document: {}, - localStorage: { - getItem: key => { - if (key === 'scannerSourceIndex') { - return `"${scannerSourceIndex}"`; - } - if (key === 'scannerSourceName') { - return `"${scannerSourceName}"`; - } + global.window.localStorage.getItem = key => { + if (key === 'scannerSourceIndex') { + return `"${scannerSourceIndex}"`; + } - return null; - }, - removeItem: () => null, - setItem: () => null, - }, - }; - - global.File = File; - global.Blob = Blob; + if (key === 'scannerSourceName') { + return `"${scannerSourceName}"`; + } - test.setState('constants', { - CASE_CAPTION_POSTFIX: Case.CASE_CAPTION_POSTFIX, - COUNTRY_TYPES: ContactFactory.COUNTRY_TYPES, - MAX_FILE_SIZE_MB, - PARTY_TYPES: ContactFactory.PARTY_TYPES, - TRIAL_CITIES: TrialSession.TRIAL_CITIES, - USER_ROLES: User.ROLES, - }); + return null; + }; }); loginAs(test, 'petitioner'); diff --git a/web-client/integration-tests/docketClerkEditsDocketEntryMeta.test.js b/web-client/integration-tests/docketClerkEditsDocketEntryMeta.test.js index 9d511e954ee..2b33b7793fc 100644 --- a/web-client/integration-tests/docketClerkEditsDocketEntryMeta.test.js +++ b/web-client/integration-tests/docketClerkEditsDocketEntryMeta.test.js @@ -7,7 +7,7 @@ import { docketClerkEditsDocketEntryMeta } from './journey/docketClerkEditsDocke import { docketClerkNavigatesToEditDocketEntryMeta } from './journey/docketClerkNavigatesToEditDocketEntryMeta'; import { docketClerkNavigatesToEditDocketEntryMetaCourtIssued } from './journey/docketClerkNavigatesToEditDocketEntryMetaCourtIssued'; import { docketClerkQCsDocketEntry } from './journey/docketClerkQCsDocketEntry'; -import { docketClerkServesOrder } from './journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from './journey/docketClerkServesDocument'; import { docketClerkVerifiesDocketEntryMetaUpdates } from './journey/docketClerkVerifiesDocketEntryMetaUpdates'; import { docketClerkVerifiesEditCourtIssuedNonstandardFields } from './journey/docketClerkVerifiesEditCourtIssuedNonstandardFields'; @@ -54,7 +54,7 @@ describe("Docket Clerk Edits a Docket Entry's Meta", () => { expectedDocumentType: 'Order', }); docketClerkAddsDocketEntryFromOrder(test, 0); - docketClerkServesOrder(test, 0); + docketClerkServesDocument(test, 0); docketClerkNavigatesToEditDocketEntryMetaCourtIssued(test, 4); docketClerkEditsDocketEntryMetaCourtIssued(test); docketClerkVerifiesDocketEntryMetaCourtIssuedUpdates(test, 4); @@ -67,7 +67,7 @@ describe("Docket Clerk Edits a Docket Entry's Meta", () => { expectedDocumentType: 'Order of Dismissal', }); docketClerkAddsDocketEntryFromOrderOfDismissal(test, 1); - docketClerkServesOrder(test, 1); + docketClerkServesDocument(test, 1); docketClerkNavigatesToEditDocketEntryMetaCourtIssued(test, 5); docketClerkVerifiesEditCourtIssuedNonstandardFieldsWithJudge(test); }); diff --git a/web-client/integration-tests/docketClerkEditsPetitionPaymentFee.test.js b/web-client/integration-tests/docketClerkEditsPetitionPaymentFee.test.js index 623002fa1a3..d262c346f8b 100644 --- a/web-client/integration-tests/docketClerkEditsPetitionPaymentFee.test.js +++ b/web-client/integration-tests/docketClerkEditsPetitionPaymentFee.test.js @@ -1,4 +1,5 @@ import { Case } from '../../shared/src/business/entities/cases/Case'; +import { PAYMENT_STATUS } from '../../shared/src/business/entities/EntityConstants'; import { loginAs, setupTest, uploadPetition } from './helpers'; const test = setupTest(); @@ -26,7 +27,7 @@ describe('docket clerk edits a petition payment fee', () => { expect(test.getState('caseDetail.petitionPaymentDate')).toBeUndefined(); expect(test.getState('caseDetail.petitionPaymentStatus')).toEqual( - Case.PAYMENT_STATUS.UNPAID, + PAYMENT_STATUS.UNPAID, ); expect(test.getState('caseDetail.docketRecord')).not.toContainEqual({ description: 'Filing Fee Paid', @@ -37,7 +38,7 @@ describe('docket clerk edits a petition payment fee', () => { await test.runSequence('updateFormValueSequence', { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.PAID, + value: PAYMENT_STATUS.PAID, }); await test.runSequence('updatePetitionDetailsSequence'); @@ -71,7 +72,7 @@ describe('docket clerk edits a petition payment fee', () => { expect(test.getState('validationErrors')).toEqual({}); expect(test.getState('caseDetail.petitionPaymentStatus')).toEqual( - Case.PAYMENT_STATUS.PAID, + PAYMENT_STATUS.PAID, ); expect(test.getState('caseDetail.petitionPaymentDate')).toEqual( '2001-01-01T05:00:00.000Z', diff --git a/web-client/integration-tests/docketClerkEditsPetitionerInformation.test.js b/web-client/integration-tests/docketClerkEditsPetitionerInformation.test.js index 8042886a567..798fbe837b4 100644 --- a/web-client/integration-tests/docketClerkEditsPetitionerInformation.test.js +++ b/web-client/integration-tests/docketClerkEditsPetitionerInformation.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { loginAs, setupTest, uploadPetition } from './helpers'; const test = setupTest(); @@ -23,7 +23,7 @@ describe('docket clerk edits the petitioner information', () => { postalCode: '77546', state: 'CT', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; diff --git a/web-client/integration-tests/docketClerkExternalDocumentQCWorkflow.test.js b/web-client/integration-tests/docketClerkExternalDocumentQCWorkflow.test.js index d73fab3b8a9..b1d11403d66 100644 --- a/web-client/integration-tests/docketClerkExternalDocumentQCWorkflow.test.js +++ b/web-client/integration-tests/docketClerkExternalDocumentQCWorkflow.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { assignWorkItems, findWorkItemByCaseId, @@ -50,7 +50,7 @@ describe('Create a work item', () => { postalCode: '77546', state: 'CT', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); }); diff --git a/web-client/integration-tests/docketClerkOrderAdvancedSearch.test.js b/web-client/integration-tests/docketClerkOrderAdvancedSearch.test.js index ef897eba8d2..243ebf9afac 100644 --- a/web-client/integration-tests/docketClerkOrderAdvancedSearch.test.js +++ b/web-client/integration-tests/docketClerkOrderAdvancedSearch.test.js @@ -3,7 +3,7 @@ import { docketClerkAddsDocketEntryFromOrder } from './journey/docketClerkAddsDo import { docketClerkAddsDocketEntryFromOrderOfDismissal } from './journey/docketClerkAddsDocketEntryFromOrderOfDismissal'; import { docketClerkCreatesAnOrder } from './journey/docketClerkCreatesAnOrder'; import { docketClerkSealsCase } from './journey/docketClerkSealsCase'; -import { docketClerkServesOrder } from './journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from './journey/docketClerkServesDocument'; import { loginAs, refreshElasticsearchIndex, @@ -65,7 +65,7 @@ describe('docket clerk order advanced search', () => { signedAtFormatted: '01/02/2020', }); docketClerkAddsDocketEntryFromOrder(test, 0); - docketClerkServesOrder(test, 0); + docketClerkServesDocument(test, 0); docketClerkCreatesAnOrder(test, { documentTitle: 'Order of Dismissal', @@ -80,7 +80,7 @@ describe('docket clerk order advanced search', () => { expectedDocumentType: 'Order of Dismissal', }); docketClerkAddsDocketEntryFromOrderOfDismissal(test, 2); - docketClerkServesOrder(test, 2); + docketClerkServesDocument(test, 2); docketClerkCreatesAnOrder(test, { documentTitle: 'Order of something', @@ -88,7 +88,7 @@ describe('docket clerk order advanced search', () => { expectedDocumentType: 'Order', }); docketClerkAddsDocketEntryFromOrder(test, 3); - docketClerkServesOrder(test, 3); + docketClerkServesDocument(test, 3); docketClerkSealsCase(test); }); @@ -318,7 +318,7 @@ describe('docket clerk order advanced search', () => { await test.runSequence('submitOrderAdvancedSearchSequence'); - await wait(1000); + await wait(2000); expect(test.getState('searchResults')).toEqual( expect.arrayContaining([ diff --git a/web-client/integration-tests/docketClerkSealsCase.test.js b/web-client/integration-tests/docketClerkSealsCase.test.js index 2ee567200c1..c71e3555d73 100644 --- a/web-client/integration-tests/docketClerkSealsCase.test.js +++ b/web-client/integration-tests/docketClerkSealsCase.test.js @@ -1,9 +1,8 @@ -import { loginAs, setupTest, uploadPetition } from './helpers'; - -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { associatedUserAdvancedSearchForSealedCase } from './journey/associatedUserAdvancedSearchForSealedCase'; import { associatedUserViewsCaseDetailForSealedCase } from './journey/associatedUserViewsCaseDetailForSealedCase'; import { docketClerkSealsCase } from './journey/docketClerkSealsCase'; +import { loginAs, setupTest, uploadPetition } from './helpers'; import { petitionsClerkAddsPractitionersToCase } from './journey/petitionsClerkAddsPractitionersToCase'; import { petitionsClerkAddsRespondentsToCase } from './journey/petitionsClerkAddsRespondentsToCase'; import { petitionsClerkViewsCaseDetail } from './journey/petitionsClerkViewsCaseDetail'; @@ -30,7 +29,7 @@ describe('Docket Clerk seals a case', () => { postalCode: '77546', state: 'CT', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; diff --git a/web-client/integration-tests/docketClerkServesCourtIssuedDocument.test.js b/web-client/integration-tests/docketClerkServesCourtIssuedDocument.test.js index 14fd3bb9b88..ef5250968f4 100644 --- a/web-client/integration-tests/docketClerkServesCourtIssuedDocument.test.js +++ b/web-client/integration-tests/docketClerkServesCourtIssuedDocument.test.js @@ -5,7 +5,7 @@ import { docketClerkAddsDocketEntryFromOrder } from './journey/docketClerkAddsDo import { docketClerkAddsDocketEntryFromOrderOfDismissal } from './journey/docketClerkAddsDocketEntryFromOrderOfDismissal'; import { docketClerkCancelsAddDocketEntryFromOrder } from './journey/docketClerkCancelsAddDocketEntryFromOrder'; import { docketClerkCreatesAnOrder } from './journey/docketClerkCreatesAnOrder'; -import { docketClerkServesOrder } from './journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from './journey/docketClerkServesDocument'; import { docketClerkViewsCaseDetailAfterServingCourtIssuedDocument } from './journey/docketClerkViewsCaseDetailAfterServingCourtIssuedDocument'; import { docketClerkViewsCaseDetailForCourtIssuedDocketEntry } from './journey/docketClerkViewsCaseDetailForCourtIssuedDocketEntry'; import { docketClerkViewsDraftOrder } from './journey/docketClerkViewsDraftOrder'; @@ -61,8 +61,8 @@ describe('Docket Clerk Adds Court-Issued Order to Docket Record', () => { docketClerkViewsCaseDetailForCourtIssuedDocketEntry(test); docketClerkViewsSavedCourtIssuedDocketEntryInProgress(test, 1); docketClerkViewsCaseDetailForCourtIssuedDocketEntry(test); - docketClerkServesOrder(test, 0); + docketClerkServesDocument(test, 0); docketClerkViewsCaseDetailAfterServingCourtIssuedDocument(test, 0); - docketClerkServesOrder(test, 1); + docketClerkServesDocument(test, 1); docketClerkViewsCaseDetailAfterServingCourtIssuedDocument(test, 1); }); diff --git a/web-client/integration-tests/docketClerkServesCourtIssuedDocumentForPaperCase.test.js b/web-client/integration-tests/docketClerkServesCourtIssuedDocumentForPaperCase.test.js index 1811eb75a9c..c09375c9465 100644 --- a/web-client/integration-tests/docketClerkServesCourtIssuedDocumentForPaperCase.test.js +++ b/web-client/integration-tests/docketClerkServesCourtIssuedDocumentForPaperCase.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { fakeFile, loginAs, setupTest } from './helpers'; // docketClerk @@ -42,6 +42,6 @@ describe('Docket Clerk Adds Court-Issued Order to Docket Record', () => { docketClerkViewsCaseDetailAfterServingCourtIssuedDocument( test, 0, - Case.STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.generalDocket, ); }); diff --git a/web-client/integration-tests/docketClerkUpdatesDocketEntries.test.js b/web-client/integration-tests/docketClerkUpdatesDocketEntries.test.js index 1de7bba48f9..bdc48d3813c 100644 --- a/web-client/integration-tests/docketClerkUpdatesDocketEntries.test.js +++ b/web-client/integration-tests/docketClerkUpdatesDocketEntries.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { docketClerkAddsDocketEntryWithoutFile } from './journey/docketClerkAddsDocketEntryWithoutFile'; import { docketClerkEditsDocketEntryNonstandardA } from './journey/docketClerkEditsDocketEntryNonstandardA'; import { docketClerkEditsDocketEntryNonstandardB } from './journey/docketClerkEditsDocketEntryNonstandardB'; @@ -32,7 +32,7 @@ describe('docket clerk updates docket entries', () => { postalCode: '77546', state: 'AZ', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; diff --git a/web-client/integration-tests/documentServiceEmailSentOnServe.test.js b/web-client/integration-tests/documentServiceEmailSentOnServe.test.js index 3ee6091ea07..449571e44a8 100644 --- a/web-client/integration-tests/documentServiceEmailSentOnServe.test.js +++ b/web-client/integration-tests/documentServiceEmailSentOnServe.test.js @@ -7,7 +7,7 @@ import { import { docketClerkAddsDocketEntryFromOrder } from './journey/docketClerkAddsDocketEntryFromOrder'; import { docketClerkCreatesAnOrder } from './journey/docketClerkCreatesAnOrder'; -import { docketClerkServesOrder } from './journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from './journey/docketClerkServesDocument'; import { docketClerkViewsCaseDetailForCourtIssuedDocketEntry } from './journey/docketClerkViewsCaseDetailForCourtIssuedDocketEntry'; import { docketClerkViewsDraftOrder } from './journey/docketClerkViewsDraftOrder'; @@ -38,7 +38,7 @@ describe.skip('Document Service Email Sent on Serve', () => { docketClerkViewsCaseDetailForCourtIssuedDocketEntry(test); docketClerkViewsDraftOrder(test, 0); docketClerkAddsDocketEntryFromOrder(test, 0); - docketClerkServesOrder(test, 0); + docketClerkServesDocument(test, 0); it('should send the expected emails for parties', async () => { const emails = await getEmailsForAddress('petitioner'); diff --git a/web-client/integration-tests/externalUserOrderAdvancedSearch.test.js b/web-client/integration-tests/externalUserOrderAdvancedSearch.test.js index 923f866bd78..351106a3b3f 100644 --- a/web-client/integration-tests/externalUserOrderAdvancedSearch.test.js +++ b/web-client/integration-tests/externalUserOrderAdvancedSearch.test.js @@ -2,7 +2,7 @@ import { associatedUserSearchesForServedOrder } from './journey/associatedUserSe import { docketClerkAddsDocketEntryFromOrder } from './journey/docketClerkAddsDocketEntryFromOrder'; import { docketClerkCreatesAnOrder } from './journey/docketClerkCreatesAnOrder'; import { docketClerkSealsCase } from './journey/docketClerkSealsCase'; -import { docketClerkServesOrder } from './journey/docketClerkServesOrder'; +import { docketClerkServesDocument } from './journey/docketClerkServesDocument'; import { loginAs, refreshElasticsearchIndex, @@ -47,7 +47,7 @@ describe('external users perform an advanced search for orders', () => { expectedDocumentType: 'Order', }); docketClerkAddsDocketEntryFromOrder(test, 0); - docketClerkServesOrder(test, 0); + docketClerkServesDocument(test, 0); it('refresh elasticsearch index', async () => { await refreshElasticsearchIndex(); }); diff --git a/web-client/integration-tests/externalUserViewsOpenAndClosedCases.test.js b/web-client/integration-tests/externalUserViewsOpenAndClosedCases.test.js new file mode 100644 index 00000000000..8dacba4c4f2 --- /dev/null +++ b/web-client/integration-tests/externalUserViewsOpenAndClosedCases.test.js @@ -0,0 +1,33 @@ +import { docketClerkUpdatesCaseStatusToClosed } from './journey/docketClerkUpdatesCaseStatusToClosed'; +import { irsPractitionerViewsOpenAndClosedCases } from './journey/irsPractitionerViewsOpenAndClosedCases'; +import { loginAs, setupTest, uploadPetition } from './helpers'; +import { petitionerViewsOpenAndClosedCases } from './journey/petitionerViewsOpenAndClosedCases'; +import { privatePractitionerViewsOpenAndClosedCases } from './journey/privatePractitionerViewsOpenClosedCases'; + +const test = setupTest(); + +describe('external user views open and closed cases', () => { + beforeAll(() => { + jest.setTimeout(30000); + loginAs(test, 'docketclerk'); + }); + + loginAs(test, 'petitioner'); + it('login as a petitioner and create the case to close', async () => { + const caseDetail = await uploadPetition(test); + expect(caseDetail.docketNumber).toBeDefined(); + test.docketNumber = caseDetail.docketNumber; + }); + + loginAs(test, 'docketclerk'); + docketClerkUpdatesCaseStatusToClosed(test); + + loginAs(test, 'petitioner'); + petitionerViewsOpenAndClosedCases(test); + + loginAs(test, 'privatePractitioner'); + privatePractitionerViewsOpenAndClosedCases(test); + + loginAs(test, 'irsPractitioner'); + irsPractitionerViewsOpenAndClosedCases(test); +}); diff --git a/web-client/integration-tests/helpers.js b/web-client/integration-tests/helpers.js index dbf5ab39427..f5cf2473bec 100644 --- a/web-client/integration-tests/helpers.js +++ b/web-client/integration-tests/helpers.js @@ -1,6 +1,8 @@ /* eslint-disable jest/no-export */ -import { CerebralTest } from 'cerebral/test'; +import { CerebralTest, runCompute } from 'cerebral/test'; +import { DynamoDB } from 'aws-sdk'; import { JSDOM } from 'jsdom'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../src/applicationContext'; import { back, @@ -10,8 +12,6 @@ import { revokeObjectURL, router, } from '../src/router'; - -import { DynamoDB } from 'aws-sdk'; import { formattedWorkQueue as formattedWorkQueueComputed } from '../src/presenter/computeds/formattedWorkQueue'; import { getScannerInterface } from '../../shared/src/persistence/dynamsoft/getScannerMockInterface'; import { @@ -20,7 +20,6 @@ import { } from '../../shared/src/business/useCases/scannerMockFiles'; import { isFunction, mapValues } from 'lodash'; import { presenter } from '../src/presenter/presenter'; -import { runCompute } from 'cerebral/test'; import { socketProvider } from '../src/providers/socket'; import { socketRouter } from '../src/providers/socketRouter'; import { userMap } from '../../shared/src/test/mockUserTokenMap'; @@ -31,9 +30,6 @@ import axios from 'axios'; import { workQueueHelper as workQueueHelperComputed } from '../src/presenter/computeds/workQueueHelper'; import FormData from 'form-data'; -const { - ContactFactory, -} = require('../../shared/src/business/entities/contacts/ContactFactory'); const formattedWorkQueue = withAppContextDecorator(formattedWorkQueueComputed); const workQueueHelper = withAppContextDecorator(workQueueHelperComputed); @@ -384,7 +380,7 @@ export const uploadPetition = async ( contactSecondary: overrides.contactSecondary || {}, filingType: 'Myself', hasIrsNotice: false, - partyType: overrides.partyType || ContactFactory.PARTY_TYPES.petitioner, + partyType: overrides.partyType || PARTY_TYPES.petitioner, preferredTrialCity: overrides.preferredTrialCity || 'Seattle, Washington', procedureType: overrides.procedureType || 'Regular', }; @@ -562,6 +558,7 @@ export const setupTest = ({ useCases = {} } = {}) => { export const gotoRoute = (routes, routeToGoTo) => { for (let route of routes) { + // eslint-disable-next-line security/detect-non-literal-regexp const regex = new RegExp( route.route.replace(/\*/g, '([a-z\\-A-Z0-9]+)').replace(/\.\./g, '(.*)') + '$', diff --git a/web-client/integration-tests/journey/adcMarksStipulatedWorkItemAsCompleted.js b/web-client/integration-tests/journey/adcMarksStipulatedWorkItemAsCompleted.js deleted file mode 100644 index 9b6fca94380..00000000000 --- a/web-client/integration-tests/journey/adcMarksStipulatedWorkItemAsCompleted.js +++ /dev/null @@ -1,26 +0,0 @@ -import { extractedDocument as extractedDocumentComputed } from '../../src/presenter/computeds/extractDocument'; -import { runCompute } from 'cerebral/test'; -import { withAppContextDecorator } from '../../src/withAppContext'; - -const extractedDocument = withAppContextDecorator(extractedDocumentComputed); - -export const adcMarksStipulatedWorkItemAsCompleted = test => { - return it('ADC marks the work item as completed', async () => { - await test.runSequence('updateCompleteFormValueSequence', { - key: 'completeMessage', - value: 'good job', - workItemId: test.stipulatedDecisionWorkItemId, - }); - await test.runSequence('submitCompleteSequence', { - workItemId: test.stipulatedDecisionWorkItemId, - }); - const document = runCompute(extractedDocument, { - state: test.getState(), - }); - - const workItem = document.workItems.find( - item => item.workItemId === test.stipulatedDecisionWorkItemId, - ); - expect(workItem).toBeUndefined(); - }); -}; diff --git a/web-client/integration-tests/journey/adcViewsCaseDetailAfterComplete.js b/web-client/integration-tests/journey/adcMarksWorkItemCompleteAndViewsCaseDetailAfterComplete.js similarity index 65% rename from web-client/integration-tests/journey/adcViewsCaseDetailAfterComplete.js rename to web-client/integration-tests/journey/adcMarksWorkItemCompleteAndViewsCaseDetailAfterComplete.js index d1b06acd209..3dcc90cebde 100644 --- a/web-client/integration-tests/journey/adcViewsCaseDetailAfterComplete.js +++ b/web-client/integration-tests/journey/adcMarksWorkItemCompleteAndViewsCaseDetailAfterComplete.js @@ -6,8 +6,17 @@ const extractedPendingMessagesFromCaseDetail = withAppContextDecorator( extractedPendingMessagesFromCaseDetailComputed, ); -export const adcViewsCaseDetailAfterComplete = test => { - return it('ADC views case detail', async () => { +export const adcMarksWorkItemCompleteAndViewsCaseDetailAfterComplete = test => { + return it('ADC marks stipulated work item as completed and views case detail', async () => { + await test.runSequence('updateCompleteFormValueSequence', { + key: 'completeMessage', + value: 'good job', + workItemId: test.stipulatedDecisionWorkItemId, + }); + await test.runSequence('submitCompleteSequence', { + workItemId: test.stipulatedDecisionWorkItemId, + }); + test.setState('caseDetail', {}); await test.runSequence('gotoCaseDetailSequence', { docketNumber: test.docketNumber, diff --git a/web-client/integration-tests/journey/captureCreatedCase.js b/web-client/integration-tests/journey/captureCreatedCase.js index 3d8a2bd21e2..0374a5bd230 100644 --- a/web-client/integration-tests/journey/captureCreatedCase.js +++ b/web-client/integration-tests/journey/captureCreatedCase.js @@ -5,7 +5,7 @@ export const captureCreatedCase = ( ) => { return it('Capture Created Case', async () => { await test.runSequence('gotoDashboardSequence'); - createdCases.push(`${test.getState('cases.0.caseId')}`); - createdDocketNumbers.push(`${test.getState('cases.0.docketNumber')}`); + createdCases.push(`${test.getState('openCases.0.caseId')}`); + createdDocketNumbers.push(`${test.getState('openCases.0.docketNumber')}`); }); }; diff --git a/web-client/integration-tests/journey/chambersUserViewsCaseDetail.js b/web-client/integration-tests/journey/chambersUserViewsCaseDetail.js index e851aae20f7..676d90a3248 100644 --- a/web-client/integration-tests/journey/chambersUserViewsCaseDetail.js +++ b/web-client/integration-tests/journey/chambersUserViewsCaseDetail.js @@ -1,4 +1,7 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { + CASE_STATUS_TYPES, + CHIEF_JUDGE, +} from '../../../shared/src/business/entities/EntityConstants'; export const chambersUserViewsCaseDetail = ( test, @@ -13,12 +16,10 @@ export const chambersUserViewsCaseDetail = ( expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); - expect(test.getState('caseDetail.status')).toEqual(Case.STATUS_TYPES.new); + expect(test.getState('caseDetail.status')).toEqual(CASE_STATUS_TYPES.new); expect(test.getState('caseDetail.documents').length).toEqual( expectedDocumentCount, ); - expect(test.getState('caseDetail.associatedJudge')).toEqual( - Case.CHIEF_JUDGE, - ); + expect(test.getState('caseDetail.associatedJudge')).toEqual(CHIEF_JUDGE); }); }; diff --git a/web-client/integration-tests/journey/chambersUserViewsCaseDetailAfterAddingOrder.js b/web-client/integration-tests/journey/chambersUserViewsCaseDetailAfterAddingOrder.js index 9c4e9dc8829..f65e6c19220 100644 --- a/web-client/integration-tests/journey/chambersUserViewsCaseDetailAfterAddingOrder.js +++ b/web-client/integration-tests/journey/chambersUserViewsCaseDetailAfterAddingOrder.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const chambersUserViewsCaseDetailAfterAddingOrder = test => { return it('Chambers user views case detail after adding order', async () => { @@ -10,7 +10,7 @@ export const chambersUserViewsCaseDetailAfterAddingOrder = test => { expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); - expect(test.getState('caseDetail.status')).toEqual(Case.STATUS_TYPES.new); + expect(test.getState('caseDetail.status')).toEqual(CASE_STATUS_TYPES.new); expect(test.getState('caseDetail.documents').length).toEqual(3); expect( test diff --git a/web-client/integration-tests/journey/docketClerkAddsCorrespondence.js b/web-client/integration-tests/journey/docketClerkAddsCorrespondence.js new file mode 100644 index 00000000000..6cdddf9a802 --- /dev/null +++ b/web-client/integration-tests/journey/docketClerkAddsCorrespondence.js @@ -0,0 +1,27 @@ +import { fakeFile } from '../helpers'; + +export const docketClerkAddsCorrespondence = (test, correspondenceTitle) => + it('docketclerk adds correspondence to case', async () => { + await test.runSequence('updateFormValueSequence', { + key: 'documentTitle', + value: correspondenceTitle, + }); + + await test.runSequence('updateFormValueSequence', { + key: 'primaryDocumentFile', + value: fakeFile, + }); + + await test.runSequence('uploadCorrespondenceDocumentSequence'); + + expect(test.getState('validationErrors')).toEqual({}); + expect(test.getState('caseDetail.correspondence')).not.toBe([]); + expect(test.getState('caseDetail.correspondence')).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + documentTitle: 'My correspondence', + }), + ]), + ); + expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); + }); diff --git a/web-client/integration-tests/journey/docketClerkAddsDocketEntryFromOrderOfDismissal.js b/web-client/integration-tests/journey/docketClerkAddsDocketEntryFromOrderOfDismissal.js index 7071da58c0f..99932332636 100644 --- a/web-client/integration-tests/journey/docketClerkAddsDocketEntryFromOrderOfDismissal.js +++ b/web-client/integration-tests/journey/docketClerkAddsDocketEntryFromOrderOfDismissal.js @@ -40,7 +40,7 @@ export const docketClerkAddsDocketEntryFromOrderOfDismissal = ( expect(test.getState('form.eventCode')).toEqual('OD'); expect(test.getState('form.documentType')).toEqual( - 'OD - Order of Dismissal Entered,', + 'OD - Order of Dismissal Entered', ); expect(helperComputed.showJudge).toBeTruthy(); expect(test.getState('form.judge')).toBeFalsy(); diff --git a/web-client/integration-tests/journey/docketClerkEditsDocketEntryFromOrderTypeB.js b/web-client/integration-tests/journey/docketClerkConvertsAnOrderToAnOpinion.js similarity index 98% rename from web-client/integration-tests/journey/docketClerkEditsDocketEntryFromOrderTypeB.js rename to web-client/integration-tests/journey/docketClerkConvertsAnOrderToAnOpinion.js index 20a5a4ea3b7..9b75a7ed2b1 100644 --- a/web-client/integration-tests/journey/docketClerkEditsDocketEntryFromOrderTypeB.js +++ b/web-client/integration-tests/journey/docketClerkConvertsAnOrderToAnOpinion.js @@ -3,7 +3,7 @@ import { formattedCaseDetail } from '../../src/presenter/computeds/formattedCase import { runCompute } from 'cerebral/test'; import { withAppContextDecorator } from '../../src/withAppContext'; -export const docketClerkEditsDocketEntryFromOrderTypeB = ( +export const docketClerkConvertsAnOrderToAnOpinion = ( test, draftOrderIndex, ) => { diff --git a/web-client/integration-tests/journey/docketClerkDeletesCorrespondence.js b/web-client/integration-tests/journey/docketClerkDeletesCorrespondence.js new file mode 100644 index 00000000000..97b981f5538 --- /dev/null +++ b/web-client/integration-tests/journey/docketClerkDeletesCorrespondence.js @@ -0,0 +1,15 @@ +export const docketClerkDeletesCorrespondence = (test, correspondenceTitle) => + it('docketclerk deletes correspondence', async () => { + await test.runSequence('openConfirmDeleteCorrespondenceModalSequence', { + documentId: test.correspondenceDocument.documentId, + documentTitle: correspondenceTitle, + }); + + expect(test.getState('modal.showModal')).toEqual( + 'DeleteCorrespondenceModal', + ); + + await test.runSequence('deleteCorrespondenceDocumentSequence'); + + expect(test.getState('caseDetail.correspondence')).toEqual([]); + }); diff --git a/web-client/integration-tests/journey/docketClerkEditsCorrespondence.js b/web-client/integration-tests/journey/docketClerkEditsCorrespondence.js new file mode 100644 index 00000000000..bf5930951ec --- /dev/null +++ b/web-client/integration-tests/journey/docketClerkEditsCorrespondence.js @@ -0,0 +1,19 @@ +export const docketClerkEditsCorrespondence = test => + it('docketclerk edits the documentTitle for a correspondence', async () => { + await test.runSequence('updateFormValueSequence', { + key: 'documentTitle', + value: 'My edited correspondence', + }); + + await test.runSequence('editCorrespondenceDocumentSequence'); + + expect(test.getState('caseDetail.correspondence')).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + documentId: test.correspondenceDocument.documentId, + documentTitle: 'My edited correspondence', + }), + ]), + ); + expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); + }); diff --git a/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPetitioner.js b/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPetitioner.js index b90c3d16205..fa6c6f550aa 100644 --- a/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPetitioner.js +++ b/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPetitioner.js @@ -1,4 +1,4 @@ -import { SERVICE_INDICATOR_TYPES } from '../../../shared/src/business/entities/cases/CaseConstants'; +import { SERVICE_INDICATOR_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const docketClerkEditsServiceIndicatorForPetitioner = test => { return it('docket clerk edits service indicator for a petitioner', async () => { diff --git a/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPractitioner.js b/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPractitioner.js index 26dae8ab341..c5d9d2d0926 100644 --- a/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPractitioner.js +++ b/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForPractitioner.js @@ -1,4 +1,4 @@ -import { SERVICE_INDICATOR_TYPES } from '../../../shared/src/business/entities/cases/CaseConstants'; +import { SERVICE_INDICATOR_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const docketClerkEditsServiceIndicatorForPractitioner = test => { return it('docket clerk edits service indicator for a practitioner', async () => { diff --git a/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForRespondent.js b/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForRespondent.js index b1d4942d664..310f19ad0ae 100644 --- a/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForRespondent.js +++ b/web-client/integration-tests/journey/docketClerkEditsServiceIndicatorForRespondent.js @@ -1,4 +1,4 @@ -import { SERVICE_INDICATOR_TYPES } from '../../../shared/src/business/entities/cases/CaseConstants'; +import { SERVICE_INDICATOR_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const docketClerkEditsServiceIndicatorForRespondent = test => { return it('docket clerk edits service indicator for a respondent', async () => { diff --git a/web-client/integration-tests/journey/docketClerkNavigatesToAddCorrespondence.js b/web-client/integration-tests/journey/docketClerkNavigatesToAddCorrespondence.js new file mode 100644 index 00000000000..646bbc76d45 --- /dev/null +++ b/web-client/integration-tests/journey/docketClerkNavigatesToAddCorrespondence.js @@ -0,0 +1,8 @@ +export const docketClerkNavigatesToAddCorrespondence = test => + it('docketclerk navigates to add correspondence page', async () => { + await test.runSequence('gotoUploadCorrespondenceDocumentSequence'); + + expect(test.getState('currentPage')).toEqual( + 'UploadCorrespondenceDocument', + ); + }); diff --git a/web-client/integration-tests/journey/docketClerkNavigatesToEditCorrespondence.js b/web-client/integration-tests/journey/docketClerkNavigatesToEditCorrespondence.js new file mode 100644 index 00000000000..bee035e0784 --- /dev/null +++ b/web-client/integration-tests/journey/docketClerkNavigatesToEditCorrespondence.js @@ -0,0 +1,19 @@ +export const docketClerkNavigatesToEditCorrespondence = ( + test, + correspondenceTitle, +) => + it('docketclerk navigates to edit correspondence', async () => { + test.correspondenceDocument = test + .getState('caseDetail.correspondence') + .find( + _correspondence => + _correspondence.documentTitle === correspondenceTitle, + ); + + await test.runSequence('gotoEditCorrespondenceDocumentSequence', { + docketNumber: test.docketNumber, + documentId: test.correspondenceDocument.documentId, + }); + + expect(test.getState('currentPage')).toEqual('EditCorrespondenceDocument'); + }); diff --git a/web-client/integration-tests/journey/docketClerkServesOrder.js b/web-client/integration-tests/journey/docketClerkServesDocument.js similarity index 93% rename from web-client/integration-tests/journey/docketClerkServesOrder.js rename to web-client/integration-tests/journey/docketClerkServesDocument.js index dae03a377a9..edbc00ec9b3 100644 --- a/web-client/integration-tests/journey/docketClerkServesOrder.js +++ b/web-client/integration-tests/journey/docketClerkServesDocument.js @@ -2,7 +2,7 @@ import { formattedCaseDetail } from '../../src/presenter/computeds/formattedCase import { runCompute } from 'cerebral/test'; import { withAppContextDecorator } from '../../src/withAppContext'; -export const docketClerkServesOrder = (test, draftOrderIndex) => { +export const docketClerkServesDocument = (test, draftOrderIndex) => { return it(`Docket Clerk serves the order after the docket entry has been created ${draftOrderIndex}`, async () => { let caseDetailFormatted; diff --git a/web-client/integration-tests/journey/docketClerkSetsCaseReadyForTrial.js b/web-client/integration-tests/journey/docketClerkSetsCaseReadyForTrial.js index 8777425cadd..13e27d0878b 100644 --- a/web-client/integration-tests/journey/docketClerkSetsCaseReadyForTrial.js +++ b/web-client/integration-tests/journey/docketClerkSetsCaseReadyForTrial.js @@ -1,4 +1,6 @@ -const { Case } = require('../../../shared/src/business/entities/cases/Case'); +const { + CASE_STATUS_TYPES, +} = require('../../../shared/src/business/entities/EntityConstants'); export const docketClerkSetsCaseReadyForTrial = test => { return it('Docket clerk sets a case ready for trial', async () => { @@ -8,21 +10,21 @@ export const docketClerkSetsCaseReadyForTrial = test => { }); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.generalDocket, ); await test.runSequence('openUpdateCaseModalSequence'); await test.runSequence('updateModalValueSequence', { key: 'caseStatus', - value: Case.STATUS_TYPES.generalDocketReadyForTrial, + value: CASE_STATUS_TYPES.generalDocketReadyForTrial, }); await test.runSequence('submitUpdateCaseModalSequence'); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.generalDocketReadyForTrial, + CASE_STATUS_TYPES.generalDocketReadyForTrial, ); if (test.casesReadyForTrial) { diff --git a/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusFromCalendaredToSubmitted.js b/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusFromCalendaredToSubmitted.js index 4c7a3a95c6a..407cbad0ced 100644 --- a/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusFromCalendaredToSubmitted.js +++ b/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusFromCalendaredToSubmitted.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const docketClerkUpdatesCaseStatusFromCalendaredToSubmitted = test => { return it('Docket clerk updates case status from Calendared to Submitted with an associated judge', async () => { @@ -9,7 +9,7 @@ export const docketClerkUpdatesCaseStatusFromCalendaredToSubmitted = test => { }); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.calendared, + CASE_STATUS_TYPES.calendared, ); await test.runSequence('openUpdateCaseModalSequence'); @@ -17,16 +17,16 @@ export const docketClerkUpdatesCaseStatusFromCalendaredToSubmitted = test => { expect(test.getState('modal.showModal')).toEqual('UpdateCaseModalDialog'); expect(test.getState('modal.caseStatus')).toEqual( - Case.STATUS_TYPES.calendared, + CASE_STATUS_TYPES.calendared, ); await test.runSequence('updateModalValueSequence', { key: 'caseStatus', - value: Case.STATUS_TYPES.submitted, + value: CASE_STATUS_TYPES.submitted, }); expect(test.getState('modal.caseStatus')).toEqual( - Case.STATUS_TYPES.submitted, + CASE_STATUS_TYPES.submitted, ); // the current judge on the case is selected by default. @@ -53,7 +53,7 @@ export const docketClerkUpdatesCaseStatusFromCalendaredToSubmitted = test => { expect(test.getState('validationErrors')).toEqual({}); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.submitted, + CASE_STATUS_TYPES.submitted, ); expect(test.getState('caseDetail.associatedJudge')).toEqual('Judge Buch'); expect(test.getState('modal')).toEqual({}); diff --git a/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusToClosed.js b/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusToClosed.js new file mode 100644 index 00000000000..11a1e277d65 --- /dev/null +++ b/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusToClosed.js @@ -0,0 +1,35 @@ +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; +import { refreshElasticsearchIndex } from '../helpers'; + +export const docketClerkUpdatesCaseStatusToClosed = test => { + return it('Docket clerk updates case status to closed', async () => { + test.setState('caseDetail', {}); + + await test.runSequence('gotoCaseDetailSequence', { + docketNumber: test.docketNumber, + }); + + const currentStatus = test.getState('caseDetail.status'); + + await test.runSequence('openUpdateCaseModalSequence'); + + expect(test.getState('modal.showModal')).toEqual('UpdateCaseModalDialog'); + + expect(test.getState('modal.caseStatus')).toEqual(currentStatus); + + await test.runSequence('updateModalValueSequence', { + key: 'caseStatus', + value: CASE_STATUS_TYPES.closed, + }); + + await test.runSequence('submitUpdateCaseModalSequence'); + + await refreshElasticsearchIndex(); + + expect(test.getState('caseDetail.status')).toEqual( + CASE_STATUS_TYPES.closed, + ); + + expect(test.getState('modal')).toEqual({}); + }); +}; diff --git a/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusToReadyForTrial.js b/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusToReadyForTrial.js index 41b5c5fbd3e..40ca168a127 100644 --- a/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusToReadyForTrial.js +++ b/web-client/integration-tests/journey/docketClerkUpdatesCaseStatusToReadyForTrial.js @@ -1,4 +1,7 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { + CASE_STATUS_TYPES, + CHIEF_JUDGE, +} from '../../../shared/src/business/entities/EntityConstants'; export const docketClerkUpdatesCaseStatusToReadyForTrial = test => { return it('Docket clerk updates case status to General Docket - At Issue (Ready for Trial)', async () => { @@ -18,7 +21,7 @@ export const docketClerkUpdatesCaseStatusToReadyForTrial = test => { await test.runSequence('updateModalValueSequence', { key: 'caseStatus', - value: Case.STATUS_TYPES.generalDocket, + value: CASE_STATUS_TYPES.generalDocket, }); await test.runSequence('clearModalSequence'); @@ -33,17 +36,15 @@ export const docketClerkUpdatesCaseStatusToReadyForTrial = test => { await test.runSequence('updateModalValueSequence', { key: 'caseStatus', - value: Case.STATUS_TYPES.generalDocketReadyForTrial, + value: CASE_STATUS_TYPES.generalDocketReadyForTrial, }); await test.runSequence('submitUpdateCaseModalSequence'); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.generalDocketReadyForTrial, - ); - expect(test.getState('caseDetail.associatedJudge')).toEqual( - Case.CHIEF_JUDGE, + CASE_STATUS_TYPES.generalDocketReadyForTrial, ); + expect(test.getState('caseDetail.associatedJudge')).toEqual(CHIEF_JUDGE); expect(test.getState('modal')).toEqual({}); }); }; diff --git a/web-client/integration-tests/journey/docketClerkViewsCaseDetailAfterServingCourtIssuedDocument.js b/web-client/integration-tests/journey/docketClerkViewsCaseDetailAfterServingCourtIssuedDocument.js index ef86757fc78..cc4ed74bba5 100644 --- a/web-client/integration-tests/journey/docketClerkViewsCaseDetailAfterServingCourtIssuedDocument.js +++ b/web-client/integration-tests/journey/docketClerkViewsCaseDetailAfterServingCourtIssuedDocument.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const docketClerkViewsCaseDetailAfterServingCourtIssuedDocument = ( test, @@ -20,10 +20,10 @@ export const docketClerkViewsCaseDetailAfterServingCourtIssuedDocument = ( if (expectedCaseStatus) { expect(test.getState('caseDetail.status')).toEqual(expectedCaseStatus); } else if (orderDocument.eventCode === 'O') { - expect(test.getState('caseDetail.status')).toEqual(Case.STATUS_TYPES.new); + expect(test.getState('caseDetail.status')).toEqual(CASE_STATUS_TYPES.new); } else { expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.closed, + CASE_STATUS_TYPES.closed, ); expect(test.getState('caseDetail.highPriority')).toEqual(false); } diff --git a/web-client/integration-tests/journey/docketClerkViewsDocument.js b/web-client/integration-tests/journey/docketClerkViewsDocument.js index 35864da9a31..4f880a9e4c4 100644 --- a/web-client/integration-tests/journey/docketClerkViewsDocument.js +++ b/web-client/integration-tests/journey/docketClerkViewsDocument.js @@ -1,10 +1,3 @@ -import { runCompute } from 'cerebral/test'; - -import { extractedDocument as extractedDocumentComputed } from '../../src/presenter/computeds/extractDocument'; -import { withAppContextDecorator } from '../../src/withAppContext'; - -const extractedDocument = withAppContextDecorator(extractedDocumentComputed); - export const docketClerkViewsDocument = test => { return it('Docket clerk views document detail', async () => { await test.runSequence('gotoDocumentDetailSequence', { @@ -32,12 +25,5 @@ export const docketClerkViewsDocument = test => { fromUserId: test.selectedWorkItem.messages[0].fromUserId, message: test.selectedWorkItem.messages[0].message, }); - - const documentResult = runCompute(extractedDocument, { - state: test.getState(), - }); - expect(documentResult).toBeDefined(); - - expect(documentResult.workItems[0].createdAtFormatted).toBeDefined(); }); }; diff --git a/web-client/integration-tests/journey/docketClerkViewsInactiveCasesForTrialSession.js b/web-client/integration-tests/journey/docketClerkViewsInactiveCasesForTrialSession.js index 74a7dbf72af..f311e3f6235 100644 --- a/web-client/integration-tests/journey/docketClerkViewsInactiveCasesForTrialSession.js +++ b/web-client/integration-tests/journey/docketClerkViewsInactiveCasesForTrialSession.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; import { formattedTrialSessionDetails as formattedTrialSessionDetailsComputed } from '../../src/presenter/computeds/formattedTrialSessionDetails'; import { runCompute } from 'cerebral/test'; import { withAppContextDecorator } from '../../src/withAppContext'; @@ -25,7 +25,7 @@ export const docketClerkViewsInactiveCasesForTrialSession = test => { test.caseId, ); expect(trialSessionDetailsFormatted.inactiveCases[0].disposition).toEqual( - `Status was changed to ${Case.STATUS_TYPES.submitted}`, + `Status was changed to ${CASE_STATUS_TYPES.submitted}`, ); }); }; diff --git a/web-client/integration-tests/journey/irsPractitionerViewsOpenAndClosedCases.js b/web-client/integration-tests/journey/irsPractitionerViewsOpenAndClosedCases.js new file mode 100644 index 00000000000..4099c6ced3c --- /dev/null +++ b/web-client/integration-tests/journey/irsPractitionerViewsOpenAndClosedCases.js @@ -0,0 +1,13 @@ +import { refreshElasticsearchIndex } from '../helpers'; + +export const irsPractitionerViewsOpenAndClosedCases = test => { + return it('irs practitoner views open and closed cases', async () => { + await refreshElasticsearchIndex(); + + await test.runSequence('gotoDashboardSequence'); + + expect(test.getState('currentPage')).toEqual('DashboardRespondent'); + expect(test.getState('openCases').length).toBeGreaterThan(0); + expect(test.getState('closedCases').length).toBeGreaterThan(0); + }); +}; diff --git a/web-client/integration-tests/journey/irsSuperuserAdvancedSearchForCase.js b/web-client/integration-tests/journey/irsSuperuserAdvancedSearchForCase.js index baa8850828b..1d1c780b008 100644 --- a/web-client/integration-tests/journey/irsSuperuserAdvancedSearchForCase.js +++ b/web-client/integration-tests/journey/irsSuperuserAdvancedSearchForCase.js @@ -1,5 +1,5 @@ +import { COUNTRY_TYPES } from '../../../shared/src/business/entities/EntityConstants'; import { CaseSearch } from '../../../shared/src/business/entities/cases/CaseSearch'; -import { ContactFactory } from '../../../shared/src/business/entities/contacts/ContactFactory'; import { refreshElasticsearchIndex } from '../helpers'; export const irsSuperuserAdvancedSearchForCase = test => { @@ -50,7 +50,7 @@ export const irsSuperuserAdvancedSearchForCase = test => { await test.runSequence('updateAdvancedSearchFormValueSequence', { formType: 'caseSearchByName', key: 'countryType', - value: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, + value: COUNTRY_TYPES.INTERNATIONAL, }); await test.runSequence('submitCaseAdvancedSearchSequence'); diff --git a/web-client/integration-tests/journey/petitionerCreatesNewCase.js b/web-client/integration-tests/journey/petitionerCreatesNewCase.js index 0978f0ac21a..ac90069b4bd 100644 --- a/web-client/integration-tests/journey/petitionerCreatesNewCase.js +++ b/web-client/integration-tests/journey/petitionerCreatesNewCase.js @@ -122,6 +122,6 @@ export const petitionerCreatesNewCase = (test, fakeFile, overrides = {}) => { expect(test.getState('currentPage')).toBe('DashboardPetitioner'); - test.docketNumber = test.getState('cases.0.docketNumber'); + test.docketNumber = test.getState('openCases.0.docketNumber'); }); }; diff --git a/web-client/integration-tests/journey/petitionerCreatesNewCaseTestAllOptions.js b/web-client/integration-tests/journey/petitionerCreatesNewCaseTestAllOptions.js index df3f9b5d321..6a3787b5488 100644 --- a/web-client/integration-tests/journey/petitionerCreatesNewCaseTestAllOptions.js +++ b/web-client/integration-tests/journey/petitionerCreatesNewCaseTestAllOptions.js @@ -1,5 +1,5 @@ import { Case } from '../../../shared/src/business/entities/cases/Case'; -import { ContactFactory } from '../../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../../shared/src/business/entities/EntityConstants'; import { runCompute } from 'cerebral/test'; import { startCaseHelper as startCaseHelperComputed } from '../../src/presenter/computeds/startCaseHelper'; import { withAppContextDecorator } from '../../src/withAppContext'; @@ -282,7 +282,7 @@ export const petitionerCreatesNewCaseTestAllOptions = ( await test.runSequence('updateStartCaseFormValueSequence', { key: 'businessType', - value: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + value: PARTY_TYPES.partnershipOtherThanTaxMatters, }); result = runCompute(startCaseHelper, { @@ -304,7 +304,7 @@ export const petitionerCreatesNewCaseTestAllOptions = ( await test.runSequence('updateStartCaseFormValueSequence', { key: 'businessType', - value: ContactFactory.PARTY_TYPES.partnershipBBA, + value: PARTY_TYPES.partnershipBBA, }); result = runCompute(startCaseHelper, { @@ -338,7 +338,7 @@ export const petitionerCreatesNewCaseTestAllOptions = ( await test.runSequence('updateStartCaseFormValueSequence', { key: 'estateType', - value: ContactFactory.PARTY_TYPES.estate, + value: PARTY_TYPES.estate, }); result = runCompute(startCaseHelper, { @@ -370,7 +370,7 @@ export const petitionerCreatesNewCaseTestAllOptions = ( await test.runSequence('updateStartCaseFormValueSequence', { key: 'estateType', - value: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + value: PARTY_TYPES.estateWithoutExecutor, }); result = runCompute(startCaseHelper, { @@ -529,7 +529,7 @@ export const petitionerCreatesNewCaseTestAllOptions = ( await test.runSequence('updateStartCaseFormValueSequence', { key: 'minorIncompetentType', - value: ContactFactory.PARTY_TYPES.nextFriendForMinor, + value: PARTY_TYPES.nextFriendForMinor, }); result = runCompute(startCaseHelper, { @@ -561,7 +561,7 @@ export const petitionerCreatesNewCaseTestAllOptions = ( await test.runSequence('updateStartCaseFormValueSequence', { key: 'minorIncompetentType', - value: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + value: PARTY_TYPES.nextFriendForIncompetentPerson, }); result = runCompute(startCaseHelper, { diff --git a/web-client/integration-tests/journey/petitionerFilesADocumentForCase.js b/web-client/integration-tests/journey/petitionerFilesADocumentForCase.js index 6934ab286e3..b0490ba33cd 100644 --- a/web-client/integration-tests/journey/petitionerFilesADocumentForCase.js +++ b/web-client/integration-tests/journey/petitionerFilesADocumentForCase.js @@ -8,28 +8,31 @@ export const petitionerFilesADocumentForCase = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'category', - value: 'Miscellaneous', - }); - - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Civil Penalty Approval Form', - }); + const documentToSelect = { + category: 'Miscellaneous', + documentTitle: 'Civil Penalty Approval Form', + documentType: 'Civil Penalty Approval Form', + eventCode: 'CIVP', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual( 'Civil Penalty Approval Form', ); - expect(test.getState('form.partyPrimary')).toEqual(undefined); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { key: 'certificateOfService', value: true, diff --git a/web-client/integration-tests/journey/petitionerFilesAmendedMotion.js b/web-client/integration-tests/journey/petitionerFilesAmendedMotion.js index fe190f6c141..c2cb4db6e9a 100644 --- a/web-client/integration-tests/journey/petitionerFilesAmendedMotion.js +++ b/web-client/integration-tests/journey/petitionerFilesAmendedMotion.js @@ -50,9 +50,7 @@ export const petitionerFilesAmendedMotion = (test, fakeFile) => { value: previousDocument.documentId, }); - //TODO why does this have to be called twice? - await test.runSequence('selectDocumentSequence'); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({}); diff --git a/web-client/integration-tests/journey/petitionerFilesDocumentForCase.js b/web-client/integration-tests/journey/petitionerFilesDocumentForCase.js index 4b8306c9646..9d61de44e5b 100644 --- a/web-client/integration-tests/journey/petitionerFilesDocumentForCase.js +++ b/web-client/integration-tests/journey/petitionerFilesDocumentForCase.js @@ -6,7 +6,7 @@ export const petitionerFilesDocumentForCase = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({ category: VALIDATION_ERROR_MESSAGES.category, @@ -44,22 +44,16 @@ export const petitionerFilesDocumentForCase = (test, fakeFile) => { expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual('Answer'); - await test.runSequence('editSelectedDocumentSequence'); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { key: 'category', value: 'Motion', }); - await test.runSequence('clearWizardDataSequence', { - key: 'category', - }); - - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({ documentType: VALIDATION_ERROR_MESSAGES.documentType[1], @@ -82,7 +76,7 @@ export const petitionerFilesDocumentForCase = (test, fakeFile) => { value: 'Nonstandard H', }); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({ secondaryDocument: { @@ -113,7 +107,7 @@ export const petitionerFilesDocumentForCase = (test, fakeFile) => { value: 'Nonstandard B', }); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({ secondaryDocument: { @@ -126,11 +120,11 @@ export const petitionerFilesDocumentForCase = (test, fakeFile) => { value: 'Anything', }); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({}); diff --git a/web-client/integration-tests/journey/petitionerVerifiesConsolidatedCases.js b/web-client/integration-tests/journey/petitionerVerifiesConsolidatedCases.js index 20f37983281..51ef97fb080 100644 --- a/web-client/integration-tests/journey/petitionerVerifiesConsolidatedCases.js +++ b/web-client/integration-tests/journey/petitionerVerifiesConsolidatedCases.js @@ -1,6 +1,6 @@ export const petitionerVerifiesConsolidatedCases = test => { return it('Petitioner verifies there are consolidated cases', async () => { - const cases = test.getState('cases'); + const cases = test.getState('openCases'); const casesWithConsolidation = cases.filter( caseDetail => !!caseDetail.leadCaseId, diff --git a/web-client/integration-tests/journey/petitionerVerifiesUnconsolidatedCases.js b/web-client/integration-tests/journey/petitionerVerifiesUnconsolidatedCases.js index 71a4ee9f5f7..040d815dcef 100644 --- a/web-client/integration-tests/journey/petitionerVerifiesUnconsolidatedCases.js +++ b/web-client/integration-tests/journey/petitionerVerifiesUnconsolidatedCases.js @@ -1,6 +1,6 @@ export const petitionerVerifiesUnconsolidatedCases = test => { return it('Petitioner verifies the cases were unconsolidated', async () => { - const cases = test.getState('cases'); + const cases = test.getState('openCases'); const casesWithConsolidation = cases.filter( caseDetail => caseDetail.leadCaseId === test.leadCaseId, diff --git a/web-client/integration-tests/journey/petitionerViewsDashboard.js b/web-client/integration-tests/journey/petitionerViewsDashboard.js index 53869020d1d..c12cc577645 100644 --- a/web-client/integration-tests/journey/petitionerViewsDashboard.js +++ b/web-client/integration-tests/journey/petitionerViewsDashboard.js @@ -1,8 +1,13 @@ +import { refreshElasticsearchIndex } from '../helpers'; + export const petitionerViewsDashboard = test => { return it('petitioner views dashboard', async () => { + await refreshElasticsearchIndex(); + await test.runSequence('gotoDashboardSequence'); + expect(test.getState('currentPage')).toEqual('DashboardPetitioner'); - expect(test.getState('cases').length).toBeGreaterThan(0); - test.docketNumber = test.getState('cases.0.docketNumber'); + expect(test.getState('openCases').length).toBeGreaterThan(0); + test.docketNumber = test.getState('openCases.0.docketNumber'); }); }; diff --git a/web-client/integration-tests/journey/petitionerViewsOpenAndClosedCases.js b/web-client/integration-tests/journey/petitionerViewsOpenAndClosedCases.js new file mode 100644 index 00000000000..b0251f8e9fe --- /dev/null +++ b/web-client/integration-tests/journey/petitionerViewsOpenAndClosedCases.js @@ -0,0 +1,15 @@ +import { refreshElasticsearchIndex } from '../helpers'; + +export const petitionerViewsOpenAndClosedCases = test => { + return it('petitioner views open and closed cases', async () => { + await refreshElasticsearchIndex(); + + await test.runSequence('gotoDashboardSequence'); + + expect(test.getState('currentPage')).toEqual('DashboardPetitioner'); + expect(test.getState('openCases').length).toBeGreaterThan(0); + expect(test.getState('closedCases')[0]).toMatchObject({ + docketNumber: test.docketNumber, + }); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerk1ViewsMessageDetail.js b/web-client/integration-tests/journey/petitionsClerk1ViewsMessageDetail.js new file mode 100644 index 00000000000..079715a68c4 --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerk1ViewsMessageDetail.js @@ -0,0 +1,12 @@ +export const petitionsClerk1ViewsMessageDetail = test => { + return it('petitions clerk 1 views the message detail for the message they received', async () => { + await test.runSequence('gotoMessageDetailSequence', { + docketNumber: test.docketNumber, + messageId: test.messageId, + }); + + expect(test.getState('messageDetail')).toMatchObject({ + messageId: test.messageId, + }); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerk1ViewsMessageInbox.js b/web-client/integration-tests/journey/petitionsClerk1ViewsMessageInbox.js new file mode 100644 index 00000000000..3ba7e957e2d --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerk1ViewsMessageInbox.js @@ -0,0 +1,18 @@ +export const petitionsClerk1ViewsMessageInbox = test => { + return it('petitions clerk 1 views their messages inbox', async () => { + await test.runSequence('gotoCaseMessagesSequence', { + box: 'inbox', + queue: 'my', + }); + + const messages = test.getState('messages'); + + const foundMessage = messages.find( + message => message.subject === test.testMessageSubject, + ); + + expect(foundMessage).toBeDefined(); + + test.messageId = foundMessage.messageId; + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkAddsDeficiencyStatisticToCase.js b/web-client/integration-tests/journey/petitionsClerkAddsDeficiencyStatisticToCase.js new file mode 100644 index 00000000000..fbbb97947e8 --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkAddsDeficiencyStatisticToCase.js @@ -0,0 +1,88 @@ +import { CASE_TYPES_MAP } from '../../../shared/src/business/entities/EntityConstants'; +import { Statistic } from '../../../shared/src/business/entities/Statistic'; + +export const petitionsClerkAddsDeficiencyStatisticToCase = test => { + return it('petitions clerk adds deficiency statistic to case after QCing', async () => { + // set up case to allow statistics to be entered + await test.runSequence('gotoPetitionQcSequence', { + docketNumber: test.docketNumber, + tab: 'IrsNotice', + }); + await test.runSequence('updateFormValueSequence', { + key: 'hasVerifiedIrsNotice', + value: true, + }); + await test.runSequence('updateFormValueSequence', { + key: 'caseType', + value: CASE_TYPES_MAP.deficiency, + }); + await test.runSequence('refreshStatisticsSequence'); + await test.runSequence('updateFormValueSequence', { + key: 'statistics.0.year', + value: 2019, + }); + await test.runSequence('updateFormValueSequence', { + key: 'statistics.0.irsDeficiencyAmount', + value: 1000, + }); + await test.runSequence('updateFormValueSequence', { + key: 'statistics.0.irsTotalPenalties', + value: 100, + }); + await test.runSequence('saveSavedCaseForLaterSequence'); + + expect(test.getState('validationErrors')).toEqual({}); + + await test.runSequence('gotoAddDeficiencyStatisticsSequence', { + docketNumber: test.docketNumber, + }); + + expect(test.getState('currentPage')).toEqual('AddDeficiencyStatistics'); + + const statisticsBefore = test.getState('caseDetail.statistics'); + + expect(test.getState('form')).toEqual({ + yearOrPeriod: 'Year', + }); + + await test.runSequence('updateFormValueSequence', { + key: 'year', + value: 2019, + }); + + await test.runSequence('submitAddDeficiencyStatisticsSequence'); + + expect(test.getState('validationErrors')).toEqual({ + irsDeficiencyAmount: + Statistic.VALIDATION_ERROR_MESSAGES.irsDeficiencyAmount, + irsTotalPenalties: Statistic.VALIDATION_ERROR_MESSAGES.irsTotalPenalties, + }); + + await test.runSequence('updateFormValueSequence', { + key: 'irsDeficiencyAmount', + value: 1234, + }); + await test.runSequence('updateFormValueSequence', { + key: 'irsTotalPenalties', + value: 0, + }); + await test.runSequence('updateFormValueSequence', { + key: 'determinationDeficiencyAmount', + value: 987, + }); + await test.runSequence('updateFormValueSequence', { + key: 'determinationTotalPenalties', + value: 22.33, + }); + + await test.runSequence('submitAddDeficiencyStatisticsSequence'); + + expect(test.getState('validationErrors')).toEqual({}); + + expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); + + const statisticsAfter = test.getState('caseDetail.statistics'); + + expect(statisticsAfter.length).toEqual(statisticsBefore.length + 1); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkAddsOtherStatisticsToCase.js b/web-client/integration-tests/journey/petitionsClerkAddsOtherStatisticsToCase.js new file mode 100644 index 00000000000..52f42d31bd8 --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkAddsOtherStatisticsToCase.js @@ -0,0 +1,28 @@ +export const petitionsClerkAddsOtherStatisticsToCase = test => { + return it('petitions clerk adds other statistics to case', async () => { + await test.runSequence('gotoAddOtherStatisticsSequence', { + docketNumber: test.docketNumber, + }); + + expect(test.getState('currentPage')).toEqual('AddOtherStatistics'); + + await test.runSequence('updateFormValueSequence', { + key: 'litigationCosts', + value: 1234, + }); + + await test.runSequence('updateFormValueSequence', { + key: 'damages', + value: 5678, + }); + + await test.runSequence('submitAddOtherStatisticsSequence'); + + expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); + + expect(test.getState('caseDetail')).toMatchObject({ + damages: 5678, + litigationCosts: 1234, + }); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkAdvancedSearchForCase.js b/web-client/integration-tests/journey/petitionsClerkAdvancedSearchForCase.js index ffe9cf23ab6..c806ec7261c 100644 --- a/web-client/integration-tests/journey/petitionsClerkAdvancedSearchForCase.js +++ b/web-client/integration-tests/journey/petitionsClerkAdvancedSearchForCase.js @@ -1,5 +1,5 @@ +import { COUNTRY_TYPES } from '../../../shared/src/business/entities/EntityConstants'; import { CaseSearch } from '../../../shared/src/business/entities/cases/CaseSearch'; -import { ContactFactory } from '../../../shared/src/business/entities/contacts/ContactFactory'; import { refreshElasticsearchIndex } from '../helpers'; export const petitionsClerkAdvancedSearchForCase = test => { @@ -50,7 +50,7 @@ export const petitionsClerkAdvancedSearchForCase = test => { await test.runSequence('updateAdvancedSearchFormValueSequence', { formType: 'caseSearchByName', key: 'countryType', - value: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, + value: COUNTRY_TYPES.INTERNATIONAL, }); await test.runSequence('submitCaseAdvancedSearchSequence'); diff --git a/web-client/integration-tests/journey/petitionsClerkBlocksCase.js b/web-client/integration-tests/journey/petitionsClerkBlocksCase.js index 8c1eb1bbb89..08a49bfdb78 100644 --- a/web-client/integration-tests/journey/petitionsClerkBlocksCase.js +++ b/web-client/integration-tests/journey/petitionsClerkBlocksCase.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; import { refreshElasticsearchIndex } from '../helpers'; export const petitionsClerkBlocksCase = (test, trialLocation) => { @@ -45,7 +45,7 @@ export const petitionsClerkBlocksCase = (test, trialLocation) => { 'Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons, Deceased, Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons, Surviving Spouse, Petitioner', docketNumber: test.docketNumber, docketNumberSuffix: 'S', - status: Case.STATUS_TYPES.generalDocketReadyForTrial, + status: CASE_STATUS_TYPES.generalDocketReadyForTrial, }), ]), ); diff --git a/web-client/integration-tests/journey/petitionsClerkChangesCaseCaptionDuringQC.js b/web-client/integration-tests/journey/petitionsClerkChangesCaseCaptionDuringQC.js new file mode 100644 index 00000000000..c30d1de3cbc --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkChangesCaseCaptionDuringQC.js @@ -0,0 +1,55 @@ +export const petitionsClerkChangesCaseCaptionDuringQC = test => { + return it('Petitions clerk changes case caption for an e-filed petition during petition QC, serves it, and verifies that a docket entry is added', async () => { + await test.runSequence('gotoPetitionQcSequence', { + docketNumber: test.docketNumber, + }); + + expect(test.getState('currentPage')).toEqual('PetitionQc'); + + const initialCaption = test.getState('form.caseCaption'); + + await test.runSequence('updateFormValueAndCaseCaptionSequence', { + key: 'contactPrimary.name', + value: 'A brand new name', + }); + + expect(test.getState('form.caseCaption')).toContain('A brand new name'); + + await test.runSequence('saveSavedCaseForLaterSequence'); + + expect(test.getState('caseDetail.initialCaption')).toEqual(initialCaption); + expect(test.getState('caseDetail.caseCaption')).toContain( + 'A brand new name', + ); + + await test.runSequence('serveCaseToIrsSequence'); + + await test.runSequence('gotoMessagesSequence', { + box: 'outbox', + queue: 'my', + workQueueIsInternal: false, + }); + + const workItems = test.getState('workQueue'); + const thisWorkItem = workItems.find( + workItem => workItem.docketNumber === test.docketNumber, + ); + + expect(thisWorkItem.docketNumberWithSuffix).not.toContain('L'); + expect(thisWorkItem.caseTitle).toContain('A brand new name'); + + await test.runSequence('gotoCaseDetailSequence', { + docketNumber: test.docketNumber, + }); + + const docketRecord = test.getState('caseDetail.docketRecord'); + + //case type was changed in an earlier test + expect(docketRecord.pop().description).toContain( + 'Docket Number is amended', + ); + expect(docketRecord.pop().description).toContain( + 'Caption of case is amended', + ); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkCreatesNewCase.js b/web-client/integration-tests/journey/petitionsClerkCreatesNewCase.js index 9283f88aa01..2462ca7417b 100644 --- a/web-client/integration-tests/journey/petitionsClerkCreatesNewCase.js +++ b/web-client/integration-tests/journey/petitionsClerkCreatesNewCase.js @@ -1,5 +1,5 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; import { CaseInternal } from '../../../shared/src/business/entities/cases/CaseInternal'; +import { PAYMENT_STATUS } from '../../../shared/src/business/entities/EntityConstants'; const { VALIDATION_ERROR_MESSAGES } = CaseInternal; @@ -183,7 +183,7 @@ export const petitionsClerkCreatesNewCase = ( await test.runSequence('updatePetitionPaymentFormValueSequence', { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.UNPAID, + value: PAYMENT_STATUS.UNPAID, }); await test.runSequence('validatePetitionFromPaperSequence'); diff --git a/web-client/integration-tests/journey/petitionsClerkCreatesNewCaseFromPaper.js b/web-client/integration-tests/journey/petitionsClerkCreatesNewCaseFromPaper.js index 8870d363adb..2eb8c1b2964 100644 --- a/web-client/integration-tests/journey/petitionsClerkCreatesNewCaseFromPaper.js +++ b/web-client/integration-tests/journey/petitionsClerkCreatesNewCaseFromPaper.js @@ -1,6 +1,8 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; import { CaseInternal } from '../../../shared/src/business/entities/cases/CaseInternal'; -import { ContactFactory } from '../../../shared/src/business/entities/contacts/ContactFactory'; +import { + PARTY_TYPES, + PAYMENT_STATUS, +} from '../../../shared/src/business/entities/EntityConstants'; import { reviewSavedPetitionHelper as reviewSavedPetitionHelperComputed } from '../../src/presenter/computeds/reviewSavedPetitionHelper'; import { runCompute } from 'cerebral/test'; import { withAppContextDecorator } from '../../src/withAppContext'; @@ -90,7 +92,7 @@ export const petitionsClerkCreatesNewCaseFromPaper = ( }, { key: 'partyType', - value: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + value: PARTY_TYPES.petitionerDeceasedSpouse, }, { key: 'contactPrimary.countryType', @@ -131,7 +133,7 @@ export const petitionsClerkCreatesNewCaseFromPaper = ( }, { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.WAIVED, + value: PAYMENT_STATUS.WAIVED, }, { key: 'paymentDateWaivedDay', diff --git a/web-client/integration-tests/journey/petitionsClerkCreatesNewMessageOnCase.js b/web-client/integration-tests/journey/petitionsClerkCreatesNewMessageOnCase.js new file mode 100644 index 00000000000..6d86fb858ed --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkCreatesNewMessageOnCase.js @@ -0,0 +1,46 @@ +import { NewCaseMessage } from '../../../shared/src/business/entities/NewCaseMessage'; +import { refreshElasticsearchIndex } from '../helpers'; + +export const petitionsClerkCreatesNewMessageOnCase = test => { + return it('petitions clerk creates new message on a case', async () => { + await test.runSequence('gotoCaseDetailSequence', { + docketNumber: test.docketNumber, + }); + + await test.runSequence('openCreateCaseMessageModalSequence'); + + await test.runSequence('updateCreateCaseMessageValueInModalSequence', { + key: 'toSection', + value: 'petitions', + }); + + await test.runSequence('updateCreateCaseMessageValueInModalSequence', { + key: 'toUserId', + value: '4805d1ab-18d0-43ec-bafb-654e83405416', //petitionsclerk1 + }); + + test.testMessageSubject = `what kind of bear is best? ${Date.now()}`; + + await test.runSequence('updateCreateCaseMessageValueInModalSequence', { + key: 'subject', + value: test.testMessageSubject, + }); + + await test.runSequence('createCaseMessageSequence'); + + expect(test.getState('validationErrors')).toEqual({ + message: NewCaseMessage.VALIDATION_ERROR_MESSAGES.message, + }); + + await test.runSequence('updateCreateCaseMessageValueInModalSequence', { + key: 'message', + value: 'bears, beets, battlestar galactica', + }); + + await test.runSequence('createCaseMessageSequence'); + + expect(test.getState('validationErrors')).toEqual({}); + + await refreshElasticsearchIndex(); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkDeleteDeficiencyStatistic.js b/web-client/integration-tests/journey/petitionsClerkDeleteDeficiencyStatistic.js new file mode 100644 index 00000000000..5fd6bc03214 --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkDeleteDeficiencyStatistic.js @@ -0,0 +1,20 @@ +export const petitionsClerkDeleteDeficiencyStatistic = test => { + return it('petitions clerk deletes the deficiency statistic', async () => { + const statistics = test.getState('caseDetail.statistics'); + + const { statisticId } = statistics[0]; + + await test.runSequence('gotoEditDeficiencyStatisticSequence', { + docketNumber: test.docketNumber, + statisticId, + }); + + await test.runSequence('deleteDeficiencyStatisticsSequence'); + + expect( + test + .getState('caseDetail.statistics') + .find(s => s.statisticId === statisticId), + ).toBeUndefined(); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkDeletesOtherStatisticToCase.js b/web-client/integration-tests/journey/petitionsClerkDeletesOtherStatisticToCase.js new file mode 100644 index 00000000000..432e11c1d70 --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkDeletesOtherStatisticToCase.js @@ -0,0 +1,14 @@ +export const petitionsClerkDeletesOtherStatisticToCase = test => { + return it('petitions clerk deletes other statistic on the case', async () => { + await test.runSequence('gotoEditOtherStatisticsSequence', { + docketNumber: test.docketNumber, + }); + expect(test.getState('caseDetail.damages')).toBeDefined(); + expect(test.getState('caseDetail.litigationCosts')).toBeDefined(); + + await test.runSequence('deleteOtherStatisticsSequence'); + + expect(test.getState('caseDetail.litigationCosts')).toEqual(null); + expect(test.getState('caseDetail.damages')).toEqual(null); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkEditOtherStatisticToCase.js b/web-client/integration-tests/journey/petitionsClerkEditOtherStatisticToCase.js new file mode 100644 index 00000000000..1b1dc132be6 --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkEditOtherStatisticToCase.js @@ -0,0 +1,18 @@ +export const petitionsClerkEditOtherStatisticToCase = test => { + return it('petitions clerk edits other statistic on the case', async () => { + await test.runSequence('gotoEditOtherStatisticsSequence', { + docketNumber: test.docketNumber, + }); + expect(test.getState('caseDetail.damages')).toEqual(5678); + expect(test.getState('caseDetail.litigationCosts')).toEqual(1234); + + await test.runSequence('updateFormValueSequence', { + key: 'litigationCosts', + value: 99, + }); + + await test.runSequence('submitEditOtherStatisticsSequence'); + + expect(test.getState('caseDetail.litigationCosts')).toEqual(99); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkEditsAnExistingCaseAndServesCase.js b/web-client/integration-tests/journey/petitionsClerkEditsAnExistingCaseAndServesCase.js deleted file mode 100644 index 210292b018b..00000000000 --- a/web-client/integration-tests/journey/petitionsClerkEditsAnExistingCaseAndServesCase.js +++ /dev/null @@ -1,112 +0,0 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; -import { getFormattedDocumentQCSectionOutbox, wait } from '../helpers'; - -export const petitionsClerkEditsAnExistingCaseAndServesCase = test => { - it('should allow edits to an in progress case', async () => { - await test.runSequence('gotoDocumentDetailSequence', { - docketNumber: test.docketNumber, - documentId: test.documentId, - }); - - await test.runSequence('updateFormValueSequence', { - key: 'contactPrimary.name', - value: 'New Name', - }); - - await test.runSequence('validatePetitionFromPaperSequence'); - - expect(test.getState('alertError')).toBeUndefined(); - expect(test.getState('validationErrors')).toEqual({}); - }); - - it('should save edits to an in progress case', async () => { - await test.runSequence('submitPetitionFromPaperSequence'); - - expect(test.getState('currentPage')).toEqual('ReviewSavedPetition'); - - await test.runSequence('saveSavedCaseForLaterSequence'); - await wait(500); - - expect(test.getState('currentPage')).toEqual('Messages'); - }); - - it('should display a confirmation modal before serving to irs', async () => { - await test.runSequence('gotoDocumentDetailSequence', { - docketNumber: test.docketNumber, - documentId: test.documentId, - }); - await test.runSequence('openConfirmServeToIrsModalSequence'); - - expect(test.getState('modal.showModal')).toBe('ConfirmServeToIrsModal'); - }); - - it('should redirect to case detail after successfully serving to irs', async () => { - await test.runSequence('serveCaseToIrsSequence'); - await wait(500); - - expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); - expect(test.getState('modal.showModal')).toEqual( - 'PaperServiceConfirmModal', - ); - }); - - it('should add served case to individual served queue', async () => { - await test.runSequence('chooseWorkQueueSequence', { - box: 'outbox', - queue: 'my', - workQueueIsInternal: false, - }); - await wait(500); - - const workQueueToDisplay = test.getState('workQueueToDisplay'); - - expect(workQueueToDisplay.workQueueIsInternal).toBeFalsy(); - expect(workQueueToDisplay.queue).toEqual('my'); - expect(workQueueToDisplay.box).toEqual('outbox'); - - const servedCase = test - .getState('workQueue') - .find(x => x.docketNumber === test.docketNumber); - - expect(servedCase).toMatchObject({ - caseTitle: 'Mona Schultz', - }); - expect(servedCase.caseStatus).toEqual(Case.STATUS_TYPES.generalDocket); - }); - - it('should add served case to section served queue', async () => { - await test.runSequence('chooseWorkQueueSequence', { - box: 'outbox', - queue: 'section', - workQueueIsInternal: false, - }); - await wait(500); - - const sectionWorkQueueToDisplay = test.getState('workQueueToDisplay'); - - expect(sectionWorkQueueToDisplay).toMatchObject({ - box: 'outbox', - queue: 'section', - workQueueIsInternal: false, - }); - - const sectionServedCase = test - .getState('workQueue') - .find(x => x.docketNumber === test.docketNumber); - - expect(sectionServedCase).toMatchObject({ - caseTitle: 'Mona Schultz', - }); - expect(sectionServedCase.caseStatus).toEqual( - Case.STATUS_TYPES.generalDocket, - ); - }); - - it('should indicate who served the case', async () => { - const outboxItems = await getFormattedDocumentQCSectionOutbox(test); - const desiredItem = outboxItems.find( - x => x.docketNumber === test.docketNumber, - ); - expect(desiredItem.sentBy).toEqual('Test Petitionsclerk'); - }); -}; diff --git a/web-client/integration-tests/journey/petitionsClerkEditsDeficiencyStatistic.js b/web-client/integration-tests/journey/petitionsClerkEditsDeficiencyStatistic.js new file mode 100644 index 00000000000..0fc159d75ee --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkEditsDeficiencyStatistic.js @@ -0,0 +1,23 @@ +export const petitionsClerkEditsDeficiencyStatistic = test => { + return it('petitions clerk edits deficiency statistic on case', async () => { + const statistics = test.getState('caseDetail.statistics'); + + const { statisticId } = statistics[0]; + + await test.runSequence('gotoEditDeficiencyStatisticSequence', { + docketNumber: test.docketNumber, + statisticId, + }); + + await test.runSequence('updateFormValueSequence', { + key: 'irsDeficiencyAmount', + value: 1000, + }); + + await test.runSequence('submitEditDeficiencyStatisticSequence'); + + expect( + test.getState('caseDetail.statistics')[0].irsDeficiencyAmount, + ).toEqual(1000); + }); +}; diff --git a/web-client/integration-tests/journey/petitionsClerkEditsPetitionInQCIRSNotice.js b/web-client/integration-tests/journey/petitionsClerkEditsPetitionInQCIRSNotice.js index 8150bdedbfa..b4b8b45ebe9 100644 --- a/web-client/integration-tests/journey/petitionsClerkEditsPetitionInQCIRSNotice.js +++ b/web-client/integration-tests/journey/petitionsClerkEditsPetitionInQCIRSNotice.js @@ -17,7 +17,7 @@ const statisticsFormHelper = withAppContextDecorator( ); export const petitionsClerkEditsPetitionInQCIRSNotice = test => { - return it('Petitioner edits Petition IRS Notice', async () => { + return it('Petitions clerk edits Petition IRS Notice', async () => { await test.runSequence('gotoPetitionQcSequence', { docketNumber: test.docketNumber, tab: 'IrsNotice', @@ -118,9 +118,9 @@ export const petitionsClerkEditsPetitionInQCIRSNotice = test => { let errors = test.getState('validationErrors.statistics'); - expect(errors[0].deficiencyAmount).toContain('required'); - expect(errors[0].totalPenalties).toContain('required'); - expect(errors[0].year).toContain('required'); + expect(errors[0].enterAllValues).toContain( + 'Enter year, deficiency amount, and total penalties', + ); // Change between a statistic period and year await test.runSequence('updateFormValueSequence', { @@ -137,14 +137,16 @@ export const petitionsClerkEditsPetitionInQCIRSNotice = test => { true, ); + // Attempt to submit without required (period) statistics fields await test.runSequence('saveSavedCaseForLaterSequence'); errors = test.getState('validationErrors.statistics'); - expect(errors[0].deficiencyAmount).toContain('required'); - expect(errors[0].totalPenalties).toContain('required'); - expect(errors[0].year).toBeUndefined(); + expect(errors[0].enterAllValues).toContain( + 'Enter period, deficiency amount, and total penalties', + ); + // Switch back to year input await test.runSequence('updateFormValueSequence', { key: 'statistics.0.yearOrPeriod', value: 'Year', @@ -157,6 +159,14 @@ export const petitionsClerkEditsPetitionInQCIRSNotice = test => { expect(statisticsUiHelper.statisticOptions[0].showYearInput).toEqual(true); expect(statisticsUiHelper.statisticOptions[0].showPeriodInput).toBeFalsy(); + await test.runSequence('saveSavedCaseForLaterSequence'); + + errors = test.getState('validationErrors.statistics'); + + expect(errors[0].enterAllValues).toContain( + 'Enter year, deficiency amount, and total penalties', + ); + // Select calculate penalties for the first statistic await test.runSequence('showCalculatePenaltiesModalSequence', { statisticIndex: 0, @@ -215,17 +225,17 @@ export const petitionsClerkEditsPetitionInQCIRSNotice = test => { statistics = test.getState('form.statistics'); modal = test.getState('modal'); - expect(statistics[0].totalPenalties).toEqual('$6.01'); + expect(statistics[0].irsTotalPenalties).toEqual('$6.01'); expect(modal.showModal).toBeUndefined(); // Attempt to insert a non-number into currency amount inputs await test.runSequence('updateFormValueSequence', { - key: 'statistics.0.deficiencyAmount', + key: 'statistics.0.irsDeficiencyAmount', value: '$100', }); await test.runSequence('updateFormValueSequence', { - key: 'statistics.0.totalPenalties', + key: 'statistics.0.irsTotalPenalties', value: '1,000', }); @@ -233,8 +243,9 @@ export const petitionsClerkEditsPetitionInQCIRSNotice = test => { errors = test.getState('validationErrors.statistics'); - expect(errors[0].deficiencyAmount).toContain('must be a number'); - expect(errors[0].deficiencyAmount).toContain('must be a number'); + expect(errors[0].enterAllValues).toContain( + 'Enter year, deficiency amount, and total penalties', + ); // Fill out all statistics values and submit for (let i = 0; i < 12; i++) { @@ -244,12 +255,12 @@ export const petitionsClerkEditsPetitionInQCIRSNotice = test => { }); await test.runSequence('updateFormValueSequence', { - key: `statistics.${i}.deficiencyAmount`, + key: `statistics.${i}.irsDeficiencyAmount`, value: 1000 + i, }); await test.runSequence('updateFormValueSequence', { - key: `statistics.${i}.totalPenalties`, + key: `statistics.${i}.irsTotalPenalties`, value: 100 + i, }); } @@ -267,87 +278,87 @@ export const petitionsClerkEditsPetitionInQCIRSNotice = test => { expect(reviewUiHelper.formattedStatistics).toEqual([ expect.objectContaining({ - deficiencyAmount: 1000, - formattedDeficiencyAmount: '$1,000.00', - formattedTotalPenalties: '$100.00', - totalPenalties: 100, + formattedIrsDeficiencyAmount: '$1,000.00', + formattedIrsTotalPenalties: '$100.00', + irsDeficiencyAmount: 1000, + irsTotalPenalties: 100, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1001, - formattedDeficiencyAmount: '$1,001.00', - formattedTotalPenalties: '$101.00', - totalPenalties: 101, + formattedIrsDeficiencyAmount: '$1,001.00', + formattedIrsTotalPenalties: '$101.00', + irsDeficiencyAmount: 1001, + irsTotalPenalties: 101, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1002, - formattedDeficiencyAmount: '$1,002.00', - formattedTotalPenalties: '$102.00', - totalPenalties: 102, + formattedIrsDeficiencyAmount: '$1,002.00', + formattedIrsTotalPenalties: '$102.00', + irsDeficiencyAmount: 1002, + irsTotalPenalties: 102, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1003, - formattedDeficiencyAmount: '$1,003.00', - formattedTotalPenalties: '$103.00', - totalPenalties: 103, + formattedIrsDeficiencyAmount: '$1,003.00', + formattedIrsTotalPenalties: '$103.00', + irsDeficiencyAmount: 1003, + irsTotalPenalties: 103, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1004, - formattedDeficiencyAmount: '$1,004.00', - formattedTotalPenalties: '$104.00', - totalPenalties: 104, + formattedIrsDeficiencyAmount: '$1,004.00', + formattedIrsTotalPenalties: '$104.00', + irsDeficiencyAmount: 1004, + irsTotalPenalties: 104, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1005, - formattedDeficiencyAmount: '$1,005.00', - formattedTotalPenalties: '$105.00', - totalPenalties: 105, + formattedIrsDeficiencyAmount: '$1,005.00', + formattedIrsTotalPenalties: '$105.00', + irsDeficiencyAmount: 1005, + irsTotalPenalties: 105, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1006, - formattedDeficiencyAmount: '$1,006.00', - formattedTotalPenalties: '$106.00', - totalPenalties: 106, + formattedIrsDeficiencyAmount: '$1,006.00', + formattedIrsTotalPenalties: '$106.00', + irsDeficiencyAmount: 1006, + irsTotalPenalties: 106, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1007, - formattedDeficiencyAmount: '$1,007.00', - formattedTotalPenalties: '$107.00', - totalPenalties: 107, + formattedIrsDeficiencyAmount: '$1,007.00', + formattedIrsTotalPenalties: '$107.00', + irsDeficiencyAmount: 1007, + irsTotalPenalties: 107, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1008, - formattedDeficiencyAmount: '$1,008.00', - formattedTotalPenalties: '$108.00', - totalPenalties: 108, + formattedIrsDeficiencyAmount: '$1,008.00', + formattedIrsTotalPenalties: '$108.00', + irsDeficiencyAmount: 1008, + irsTotalPenalties: 108, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1009, - formattedDeficiencyAmount: '$1,009.00', - formattedTotalPenalties: '$109.00', - totalPenalties: 109, + formattedIrsDeficiencyAmount: '$1,009.00', + formattedIrsTotalPenalties: '$109.00', + irsDeficiencyAmount: 1009, + irsTotalPenalties: 109, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1010, - formattedDeficiencyAmount: '$1,010.00', - formattedTotalPenalties: '$110.00', - totalPenalties: 110, + formattedIrsDeficiencyAmount: '$1,010.00', + formattedIrsTotalPenalties: '$110.00', + irsDeficiencyAmount: 1010, + irsTotalPenalties: 110, year: 2019, }), expect.objectContaining({ - deficiencyAmount: 1011, - formattedDeficiencyAmount: '$1,011.00', - formattedTotalPenalties: '$111.00', - totalPenalties: 111, + formattedIrsDeficiencyAmount: '$1,011.00', + formattedIrsTotalPenalties: '$111.00', + irsDeficiencyAmount: 1011, + irsTotalPenalties: 111, year: 2019, }), ]); diff --git a/web-client/integration-tests/journey/petitionsClerkSubmitsCaseToIrs.js b/web-client/integration-tests/journey/petitionsClerkSubmitsCaseToIrs.js index b722e9bc95b..66866854aa0 100644 --- a/web-client/integration-tests/journey/petitionsClerkSubmitsCaseToIrs.js +++ b/web-client/integration-tests/journey/petitionsClerkSubmitsCaseToIrs.js @@ -1,3 +1,4 @@ +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; import { Case } from '../../../shared/src/business/entities/cases/Case'; const { VALIDATION_ERROR_MESSAGES } = Case; @@ -61,7 +62,7 @@ export const petitionsClerkSubmitsCaseToIrs = test => { '2017-12-24T05:00:00.000Z', ); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.generalDocket, ); //check that documents were served const documents = test.getState('caseDetail.documents'); diff --git a/web-client/integration-tests/journey/petitionsClerkSubmitsPaperCaseToIrs.js b/web-client/integration-tests/journey/petitionsClerkSubmitsPaperCaseToIrs.js index c19d9fed6f9..c2a3cc3f3d8 100644 --- a/web-client/integration-tests/journey/petitionsClerkSubmitsPaperCaseToIrs.js +++ b/web-client/integration-tests/journey/petitionsClerkSubmitsPaperCaseToIrs.js @@ -1,5 +1,8 @@ +import { + CASE_STATUS_TYPES, + ROLES, +} from '../../../shared/src/business/entities/EntityConstants'; import { Case } from '../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../shared/src/business/entities/User'; const { VALIDATION_ERROR_MESSAGES } = Case; @@ -64,14 +67,14 @@ export const petitionsClerkSubmitsPaperCaseToIrs = test => { '2017-12-24T05:00:00.000Z', ); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.generalDocket, ); //check that documents were served const documents = test.getState('caseDetail.documents'); for (const document of documents) { expect(document.servedAt).toBeDefined(); expect(document.servedParties.length).toEqual(1); - expect(document.servedParties[0].role).toEqual(User.ROLES.irsSuperuser); + expect(document.servedParties[0].role).toEqual(ROLES.irsSuperuser); } }); }; diff --git a/web-client/integration-tests/journey/petitionsClerkUpdatesCaseDetail.js b/web-client/integration-tests/journey/petitionsClerkUpdatesCaseDetail.js index 46f5cd0ce43..b8720a24db0 100644 --- a/web-client/integration-tests/journey/petitionsClerkUpdatesCaseDetail.js +++ b/web-client/integration-tests/journey/petitionsClerkUpdatesCaseDetail.js @@ -1,4 +1,5 @@ import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { PAYMENT_STATUS } from '../../../shared/src/business/entities/EntityConstants'; const { VALIDATION_ERROR_MESSAGES } = Case; @@ -27,7 +28,7 @@ export const petitionsClerkUpdatesCaseDetail = test => { key: 'irsDay', value: '24', }); - await test.runSequence('navigateToReviewSavedPetitionSequence'); + await test.runSequence('saveSavedCaseForLaterSequence'); expect(test.getState('validationErrors')).toEqual({ irsNoticeDate: VALIDATION_ERROR_MESSAGES.irsNoticeDate[1], @@ -88,7 +89,7 @@ export const petitionsClerkUpdatesCaseDetail = test => { await test.runSequence('validateCaseDetailSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('navigateToReviewSavedPetitionSequence'); + await test.runSequence('saveSavedCaseForLaterSequence'); await test.runSequence('saveSavedCaseForLaterSequence'); test.setState('caseDetail', {}); @@ -125,9 +126,9 @@ export const petitionsClerkUpdatesCaseDetail = test => { // petitionPaymentDate await test.runSequence('updateFormValueSequence', { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.PAID, + value: PAYMENT_STATUS.PAID, }); - await test.runSequence('navigateToReviewSavedPetitionSequence'); + await test.runSequence('saveSavedCaseForLaterSequence'); expect(test.getState('validationErrors')).toEqual({ petitionPaymentDate: VALIDATION_ERROR_MESSAGES.petitionPaymentDate, @@ -164,7 +165,7 @@ export const petitionsClerkUpdatesCaseDetail = test => { value: '', }); - await test.runSequence('navigateToReviewSavedPetitionSequence'); + await test.runSequence('saveSavedCaseForLaterSequence'); expect(test.getState('validationErrors')).toEqual({ caseType: VALIDATION_ERROR_MESSAGES.caseType, procedureType: VALIDATION_ERROR_MESSAGES.procedureType, @@ -187,7 +188,7 @@ export const petitionsClerkUpdatesCaseDetail = test => { value: 'Regular', }); //submit and route to case detail - await test.runSequence('navigateToReviewSavedPetitionSequence'); + await test.runSequence('saveSavedCaseForLaterSequence'); await test.runSequence('saveSavedCaseForLaterSequence'); await test.runSequence('navigateToPathSequence', { path: `/case-detail/${test.docketNumber}`, diff --git a/web-client/integration-tests/journey/petitionsClerkVerifiesOrderForOdsCheckbox.js b/web-client/integration-tests/journey/petitionsClerkVerifiesOrderForOdsCheckbox.js index b54cbea1276..e68096be014 100644 --- a/web-client/integration-tests/journey/petitionsClerkVerifiesOrderForOdsCheckbox.js +++ b/web-client/integration-tests/journey/petitionsClerkVerifiesOrderForOdsCheckbox.js @@ -1,5 +1,5 @@ import { CaseInternal } from '../../../shared/src/business/entities/cases/CaseInternal'; -import { ContactFactory } from '../../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const petitionsClerkVerifiesOrderForOdsCheckbox = (test, fakeFile) => { return it('Petitions clerk verifies that the Order for ODS checkbox is correctly checked and unchecked', async () => { @@ -9,14 +9,14 @@ export const petitionsClerkVerifiesOrderForOdsCheckbox = (test, fakeFile) => { await test.runSequence('updateStartCaseInternalPartyTypeSequence', { key: 'partyType', - value: ContactFactory.PARTY_TYPES.petitioner, + value: PARTY_TYPES.petitioner, }); expect(test.getState('form.orderForOds')).toBeFalsy(); await test.runSequence('updateStartCaseInternalPartyTypeSequence', { key: 'partyType', - value: ContactFactory.PARTY_TYPES.corporation, + value: PARTY_TYPES.corporation, }); expect(test.getState('form.orderForOds')).toBeTruthy(); diff --git a/web-client/integration-tests/journey/petitionsClerkVerifiesPetitionPaymentFeeOptions.js b/web-client/integration-tests/journey/petitionsClerkVerifiesPetitionPaymentFeeOptions.js index d033003fb2d..c2a6943b42b 100644 --- a/web-client/integration-tests/journey/petitionsClerkVerifiesPetitionPaymentFeeOptions.js +++ b/web-client/integration-tests/journey/petitionsClerkVerifiesPetitionPaymentFeeOptions.js @@ -1,5 +1,6 @@ import { Case } from '../../../shared/src/business/entities/cases/Case'; import { CaseInternal } from '../../../shared/src/business/entities/cases/CaseInternal'; +import { PAYMENT_STATUS } from '../../../shared/src/business/entities/EntityConstants'; export const petitionsClerkVerifiesPetitionPaymentFeeOptions = ( test, @@ -14,7 +15,7 @@ export const petitionsClerkVerifiesPetitionPaymentFeeOptions = ( await test.runSequence('updatePetitionPaymentFormValueSequence', { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.PAID, + value: PAYMENT_STATUS.PAID, }); expect(test.getState('form.orderForFilingFee')).toEqual(false); @@ -55,7 +56,7 @@ export const petitionsClerkVerifiesPetitionPaymentFeeOptions = ( await test.runSequence('updatePetitionPaymentFormValueSequence', { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.UNPAID, + value: PAYMENT_STATUS.UNPAID, }); expect(test.getState('form.orderForFilingFee')).toEqual(true); @@ -71,7 +72,7 @@ export const petitionsClerkVerifiesPetitionPaymentFeeOptions = ( await test.runSequence('updatePetitionPaymentFormValueSequence', { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.WAIVED, + value: PAYMENT_STATUS.WAIVED, }); expect(test.getState('form.orderForFilingFee')).toEqual(false); diff --git a/web-client/integration-tests/journey/petitionsClerkViewsCaseDetail.js b/web-client/integration-tests/journey/petitionsClerkViewsCaseDetail.js index bd2700209eb..3c15675417b 100644 --- a/web-client/integration-tests/journey/petitionsClerkViewsCaseDetail.js +++ b/web-client/integration-tests/journey/petitionsClerkViewsCaseDetail.js @@ -1,4 +1,7 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { + CASE_STATUS_TYPES, + CHIEF_JUDGE, +} from '../../../shared/src/business/entities/EntityConstants'; export const petitionsClerkViewsCaseDetail = ( test, @@ -13,13 +16,11 @@ export const petitionsClerkViewsCaseDetail = ( expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); - expect(test.getState('caseDetail.status')).toEqual(Case.STATUS_TYPES.new); + expect(test.getState('caseDetail.status')).toEqual(CASE_STATUS_TYPES.new); expect(test.getState('caseDetail.documents').length).toEqual( expectedDocumentCount, ); - expect(test.getState('caseDetail.associatedJudge')).toEqual( - Case.CHIEF_JUDGE, - ); + expect(test.getState('caseDetail.associatedJudge')).toEqual(CHIEF_JUDGE); const caseDetail = test.getState('caseDetail'); diff --git a/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingNotice.js b/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingNotice.js index e7e917f039e..5b97bc60267 100644 --- a/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingNotice.js +++ b/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingNotice.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const petitionsClerkViewsCaseDetailAfterAddingNotice = test => { return it('Petitions clerk views case detail after adding notice', async () => { @@ -8,7 +8,7 @@ export const petitionsClerkViewsCaseDetailAfterAddingNotice = test => { }); expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); - expect(test.getState('caseDetail.status')).toEqual(Case.STATUS_TYPES.new); + expect(test.getState('caseDetail.status')).toEqual(CASE_STATUS_TYPES.new); expect(test.getState('caseDetail.documents').length).toEqual(3); expect( test diff --git a/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingOrder.js b/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingOrder.js index ec1c9ede1a9..cbe84ac2986 100644 --- a/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingOrder.js +++ b/web-client/integration-tests/journey/petitionsClerkViewsCaseDetailAfterAddingOrder.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const petitionsClerkViewsCaseDetailAfterAddingOrder = test => { return it('Petitions clerk views case detail after adding order', async () => { @@ -8,7 +8,7 @@ export const petitionsClerkViewsCaseDetailAfterAddingOrder = test => { }); expect(test.getState('currentPage')).toEqual('CaseDetailInternal'); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); - expect(test.getState('caseDetail.status')).toEqual(Case.STATUS_TYPES.new); + expect(test.getState('caseDetail.status')).toEqual(CASE_STATUS_TYPES.new); expect(test.getState('caseDetail.documents').length).toEqual(3); expect( test diff --git a/web-client/integration-tests/journey/petitionsClerkViewsDocketRecordEditLinks.js b/web-client/integration-tests/journey/petitionsClerkViewsDocketRecordEditLinks.js index 6216cfbfb19..80e1ef8dbf0 100644 --- a/web-client/integration-tests/journey/petitionsClerkViewsDocketRecordEditLinks.js +++ b/web-client/integration-tests/journey/petitionsClerkViewsDocketRecordEditLinks.js @@ -16,7 +16,7 @@ export const petitionsClerkViewsDocketRecordEditLinks = test => { ); expect(caseDetailFormatted.formattedDocketEntries).toMatchObject([ - { description: 'Petition', editLink: '/review' }, + { description: 'Petition', editLink: '' }, { description: 'Request for Place of Trial at Seattle, Washington', editLink: '', diff --git a/web-client/integration-tests/journey/petitionsClerkViewsMessages.js b/web-client/integration-tests/journey/petitionsClerkViewsMessages.js index dc5c563826f..95c7cfa6529 100644 --- a/web-client/integration-tests/journey/petitionsClerkViewsMessages.js +++ b/web-client/integration-tests/journey/petitionsClerkViewsMessages.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const petitionsClerkViewsMessages = test => { return it('Petitions clerk views messages', async () => { @@ -19,7 +19,7 @@ export const petitionsClerkViewsMessages = test => { workItem.document.documentType === 'Petition', ); expect(workItem).toBeDefined(); - expect(workItem.caseStatus).toEqual(Case.STATUS_TYPES.new); + expect(workItem.caseStatus).toEqual(CASE_STATUS_TYPES.new); expect(workItem.messages[0].message).toEqual( 'Petition filed by Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons, Deceased, Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons 2, Surviving Spouse is ready for review.', ); diff --git a/web-client/integration-tests/journey/petitionsClerkViewsSentMessagesBox.js b/web-client/integration-tests/journey/petitionsClerkViewsSentMessagesBox.js new file mode 100644 index 00000000000..a42ba012c8f --- /dev/null +++ b/web-client/integration-tests/journey/petitionsClerkViewsSentMessagesBox.js @@ -0,0 +1,16 @@ +export const petitionsClerkViewsSentMessagesBox = test => { + return it('petitions clerk views their sent messages box', async () => { + await test.runSequence('gotoCaseMessagesSequence', { + box: 'outbox', + queue: 'my', + }); + + const messages = test.getState('messages'); + + const foundMessage = messages.find( + message => message.subject === test.testMessageSubject, + ); + + expect(foundMessage).toBeDefined(); + }); +}; diff --git a/web-client/integration-tests/journey/practitionerCreatesNewCase.js b/web-client/integration-tests/journey/practitionerCreatesNewCase.js index 1fdfa29eb77..ec1a13e866a 100644 --- a/web-client/integration-tests/journey/practitionerCreatesNewCase.js +++ b/web-client/integration-tests/journey/practitionerCreatesNewCase.js @@ -200,6 +200,6 @@ export const practitionerCreatesNewCase = (test, fakeFile) => { expect(test.getState('currentPage')).toBe('DashboardPractitioner'); - test.docketNumber = test.getState('cases.0.docketNumber'); + test.docketNumber = test.getState('openCases.0.docketNumber'); }); }; diff --git a/web-client/integration-tests/journey/practitionerFilesDocumentForOwnedCase.js b/web-client/integration-tests/journey/practitionerFilesDocumentForOwnedCase.js index 32ad92e5310..e7009ce9e66 100644 --- a/web-client/integration-tests/journey/practitionerFilesDocumentForOwnedCase.js +++ b/web-client/integration-tests/journey/practitionerFilesDocumentForOwnedCase.js @@ -8,21 +8,26 @@ export const practitionerFilesDocumentForOwnedCase = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'category', - value: 'Miscellaneous', - }); - - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Civil Penalty Approval Form', - }); + const documentToSelect = { + category: 'Miscellaneous', + documentTitle: 'Civil Penalty Approval Form', + documentType: 'Civil Penalty Approval Form', + eventCode: 'CIVP', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual( 'Civil Penalty Approval Form', diff --git a/web-client/integration-tests/journey/practitionerFilesDocumentForStipulatedDecision.js b/web-client/integration-tests/journey/practitionerFilesDocumentForStipulatedDecision.js index d310330b4b9..03fc8280e59 100644 --- a/web-client/integration-tests/journey/practitionerFilesDocumentForStipulatedDecision.js +++ b/web-client/integration-tests/journey/practitionerFilesDocumentForStipulatedDecision.js @@ -11,21 +11,26 @@ export const practitionerFilesDocumentForStipulatedDecision = ( docketNumber: test.docketNumber, }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'category', - value: 'Decision', - }); - - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Proposed Stipulated Decision', - }); + const documentToSelect = { + category: 'Decision', + documentTitle: 'Proposed Stipulated Decision', + documentType: 'Proposed Stipulated Decision', + eventCode: 'PSDE', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual( 'Proposed Stipulated Decision', diff --git a/web-client/integration-tests/journey/practitionerRequestsPendingAccessToCase.js b/web-client/integration-tests/journey/practitionerRequestsPendingAccessToCase.js index 148196e7b9e..5de962a0f05 100644 --- a/web-client/integration-tests/journey/practitionerRequestsPendingAccessToCase.js +++ b/web-client/integration-tests/journey/practitionerRequestsPendingAccessToCase.js @@ -83,6 +83,10 @@ export const practitionerRequestsPendingAccessToCase = (test, fakeFile) => { ); expect(test.getState('validationErrors')).toEqual({}); + expect(test.getState('wizardStep')).toBe('RequestAccessReview'); + await test.runSequence('submitCaseAssociationRequestSequence'); + + expect(test.getState('wizardStep')).toBeUndefined(); }); }; diff --git a/web-client/integration-tests/journey/practitionerViewsDashboard.js b/web-client/integration-tests/journey/practitionerViewsDashboard.js index cb3d2c53860..97f4fc2db11 100644 --- a/web-client/integration-tests/journey/practitionerViewsDashboard.js +++ b/web-client/integration-tests/journey/practitionerViewsDashboard.js @@ -2,8 +2,8 @@ export const practitionerViewsDashboard = test => { return it('Practitioner views dashboard', async () => { await test.runSequence('gotoDashboardSequence'); expect(test.getState('currentPage')).toEqual('DashboardPractitioner'); - expect(test.getState('cases').length).toBeGreaterThan(0); - const latestDocketNumber = test.getState('cases.0.docketNumber'); + expect(test.getState('openCases').length).toBeGreaterThan(0); + const latestDocketNumber = test.getState('openCases.0.docketNumber'); expect(test.docketNumber).toEqual(latestDocketNumber); }); }; diff --git a/web-client/integration-tests/journey/privatePractitionerViewsOpenClosedCases.js b/web-client/integration-tests/journey/privatePractitionerViewsOpenClosedCases.js new file mode 100644 index 00000000000..1ee4532c513 --- /dev/null +++ b/web-client/integration-tests/journey/privatePractitionerViewsOpenClosedCases.js @@ -0,0 +1,9 @@ +export const privatePractitionerViewsOpenAndClosedCases = test => { + return it('private practitioner views open and closed cases', async () => { + await test.runSequence('gotoDashboardSequence'); + + expect(test.getState('currentPage')).toEqual('DashboardPractitioner'); + expect(test.getState('openCases').length).toBeGreaterThan(0); + expect(test.getState('closedCases').length).toBe(0); + }); +}; diff --git a/web-client/integration-tests/journey/respondentAddsAnswer.js b/web-client/integration-tests/journey/respondentAddsAnswer.js index e993a0d5c4d..7d39a5b4b15 100644 --- a/web-client/integration-tests/journey/respondentAddsAnswer.js +++ b/web-client/integration-tests/journey/respondentAddsAnswer.js @@ -6,7 +6,7 @@ export const respondentAddsAnswer = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({ category: VALIDATION_ERROR_MESSAGES.category, @@ -23,16 +23,26 @@ export const respondentAddsAnswer = (test, fakeFile) => { documentType: VALIDATION_ERROR_MESSAGES.documentType[1], }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Answer', - }); + const documentToSelect = { + category: 'Answer (filed by respondent only)', + documentTitle: 'Answer', + documentType: 'Answer', + eventCode: 'A', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual('Answer'); diff --git a/web-client/integration-tests/journey/respondentAddsMotion.js b/web-client/integration-tests/journey/respondentAddsMotion.js index 999556315d7..55e408fa18a 100644 --- a/web-client/integration-tests/journey/respondentAddsMotion.js +++ b/web-client/integration-tests/journey/respondentAddsMotion.js @@ -6,7 +6,7 @@ export const respondentAddsMotion = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({ category: VALIDATION_ERROR_MESSAGES.category, @@ -23,17 +23,26 @@ export const respondentAddsMotion = (test, fakeFile) => { documentType: VALIDATION_ERROR_MESSAGES.documentType[1], }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Motion for Continuance', - }); + const documentToSelect = { + category: 'Motion', + documentTitle: 'Motion for Continuance', + documentType: 'Motion for Continuance', + eventCode: 'M006', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual( 'Motion for Continuance', diff --git a/web-client/integration-tests/journey/respondentAddsStipulatedDecision.js b/web-client/integration-tests/journey/respondentAddsStipulatedDecision.js index 9ff6b75ea35..b06e4af4fc4 100644 --- a/web-client/integration-tests/journey/respondentAddsStipulatedDecision.js +++ b/web-client/integration-tests/journey/respondentAddsStipulatedDecision.js @@ -6,7 +6,7 @@ export const respondentAddsStipulatedDecision = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('validationErrors')).toEqual({ category: VALIDATION_ERROR_MESSAGES.category, @@ -23,16 +23,26 @@ export const respondentAddsStipulatedDecision = (test, fakeFile) => { documentType: VALIDATION_ERROR_MESSAGES.documentType[1], }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Proposed Stipulated Decision', - }); + const documentToSelect = { + category: 'Decision', + documentTitle: 'Proposed Stipulated Decision', + documentType: 'Proposed Stipulated Decision', + eventCode: 'PSDE', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual( 'Proposed Stipulated Decision', diff --git a/web-client/integration-tests/journey/respondentFilesDocumentForAssociatedCase.js b/web-client/integration-tests/journey/respondentFilesDocumentForAssociatedCase.js index 10e9b718d46..b67299453a7 100644 --- a/web-client/integration-tests/journey/respondentFilesDocumentForAssociatedCase.js +++ b/web-client/integration-tests/journey/respondentFilesDocumentForAssociatedCase.js @@ -8,21 +8,26 @@ export const respondentFilesDocumentForAssociatedCase = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'category', - value: 'Miscellaneous', - }); - - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Civil Penalty Approval Form', - }); + const documentToSelect = { + category: 'Miscellaneous', + documentTitle: 'Civil Penalty Approval Form', + documentType: 'Civil Penalty Approval Form', + eventCode: 'CIVP', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual( 'Civil Penalty Approval Form', diff --git a/web-client/integration-tests/journey/respondentFilesFirstIRSDocumentOnCase.js b/web-client/integration-tests/journey/respondentFilesFirstIRSDocumentOnCase.js index 16797124724..55f0afb2bf7 100644 --- a/web-client/integration-tests/journey/respondentFilesFirstIRSDocumentOnCase.js +++ b/web-client/integration-tests/journey/respondentFilesFirstIRSDocumentOnCase.js @@ -8,21 +8,26 @@ export const respondentFilesFirstIRSDocumentOnCase = (test, fakeFile) => { docketNumber: test.docketNumber, }); - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'category', - value: 'Answer (filed by respondent only)', - }); - - await test.runSequence('updateFileDocumentWizardFormValueSequence', { - key: 'documentType', - value: 'Answer', - }); + const documentToSelect = { + category: 'Answer (filed by respondent only)', + documentTitle: 'Answer', + documentType: 'Answer', + eventCode: 'A', + scenario: 'Standard', + }; + + for (const key of Object.keys(documentToSelect)) { + await test.runSequence('updateFileDocumentWizardFormValueSequence', { + key, + value: documentToSelect[key], + }); + } await test.runSequence('validateSelectDocumentTypeSequence'); expect(test.getState('validationErrors')).toEqual({}); - await test.runSequence('selectDocumentSequence'); + await test.runSequence('completeDocumentSelectSequence'); expect(test.getState('form.documentType')).toEqual('Answer'); diff --git a/web-client/integration-tests/journey/respondentViewsCaseDetailOfBatchedCase.js b/web-client/integration-tests/journey/respondentViewsCaseDetailOfBatchedCase.js index 618121b76b6..9b27acbc14c 100644 --- a/web-client/integration-tests/journey/respondentViewsCaseDetailOfBatchedCase.js +++ b/web-client/integration-tests/journey/respondentViewsCaseDetailOfBatchedCase.js @@ -1,4 +1,4 @@ -import { Case } from '../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../shared/src/business/entities/EntityConstants'; export const respondentViewsCaseDetailOfBatchedCase = test => { return it('Respondent views case detail', async () => { @@ -9,7 +9,7 @@ export const respondentViewsCaseDetailOfBatchedCase = test => { expect(test.getState('currentPage')).toEqual('CaseDetail'); expect(test.getState('caseDetail.docketNumber')).toEqual(test.docketNumber); expect(test.getState('caseDetail.status')).toEqual( - Case.STATUS_TYPES.generalDocket, + CASE_STATUS_TYPES.generalDocket, ); expect(test.getState('caseDetail.documents').length).toEqual(2); }); diff --git a/web-client/integration-tests/journey/respondentViewsDashboard.js b/web-client/integration-tests/journey/respondentViewsDashboard.js index 42a1955df97..db2b0d53b7e 100644 --- a/web-client/integration-tests/journey/respondentViewsDashboard.js +++ b/web-client/integration-tests/journey/respondentViewsDashboard.js @@ -2,6 +2,6 @@ export const respondentViewsDashboard = test => { return it('Respondent views dashboard', async () => { await test.runSequence('gotoDashboardSequence'); expect(test.getState('currentPage')).toEqual('DashboardRespondent'); - expect(test.getState('cases').length).toBeGreaterThanOrEqual(0); + expect(test.getState('openCases').length).toBeGreaterThanOrEqual(0); }); }; diff --git a/web-client/integration-tests/messagesJourney.test.js b/web-client/integration-tests/messagesJourney.test.js new file mode 100644 index 00000000000..8b8c57882e1 --- /dev/null +++ b/web-client/integration-tests/messagesJourney.test.js @@ -0,0 +1,30 @@ +import { loginAs, setupTest, uploadPetition } from './helpers'; +import { petitionsClerk1ViewsMessageDetail } from './journey/petitionsClerk1ViewsMessageDetail'; +import { petitionsClerk1ViewsMessageInbox } from './journey/petitionsClerk1ViewsMessageInbox'; +import { petitionsClerkCreatesNewMessageOnCase } from './journey/petitionsClerkCreatesNewMessageOnCase'; +import { petitionsClerkViewsSentMessagesBox } from './journey/petitionsClerkViewsSentMessagesBox'; + +const test = setupTest(); + +describe('messages journey', () => { + beforeAll(() => { + jest.setTimeout(40000); + }); + + loginAs(test, 'petitioner'); + it('Create test case to send messages', async () => { + const caseDetail = await uploadPetition(test); + expect(caseDetail.docketNumber).toBeDefined(); + test.docketNumber = caseDetail.docketNumber; + test.documentId = caseDetail.documents[0].documentId; + test.caseId = caseDetail.caseId; + }); + + loginAs(test, 'petitionsclerk'); + petitionsClerkCreatesNewMessageOnCase(test); + petitionsClerkViewsSentMessagesBox(test); + + loginAs(test, 'petitionsclerk1'); + petitionsClerk1ViewsMessageInbox(test); + petitionsClerk1ViewsMessageDetail(test); +}); diff --git a/web-client/integration-tests/modifyContactInfo.test.js b/web-client/integration-tests/modifyContactInfo.test.js index ddae5e200a0..28cff241f47 100644 --- a/web-client/integration-tests/modifyContactInfo.test.js +++ b/web-client/integration-tests/modifyContactInfo.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { docketClerkViewsNoticeOfChangeOfAddress } from './journey/docketClerkViewsNoticeOfChangeOfAddress'; import { loginAs, setupTest, uploadPetition } from './helpers'; import { petitionerEditsCasePrimaryContactAddress } from './journey/petitionerEditsCasePrimaryContactAddress'; @@ -33,7 +33,7 @@ describe('Modify Petitioner Contact Information', () => { postalCode: '77546', state: 'CT', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; diff --git a/web-client/integration-tests/noticeOfTrialSessionWithPaperService.test.js b/web-client/integration-tests/noticeOfTrialSessionWithPaperService.test.js index 94cff3752a4..83068f008a3 100644 --- a/web-client/integration-tests/noticeOfTrialSessionWithPaperService.test.js +++ b/web-client/integration-tests/noticeOfTrialSessionWithPaperService.test.js @@ -1,12 +1,12 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; - -import { loginAs, setupTest, uploadPetition } from './helpers'; -import { markAllCasesAsQCed } from './journey/markAllCasesAsQCed'; - +import { + COUNTRY_TYPES, + PARTY_TYPES, +} from '../../shared/src/business/entities/EntityConstants'; import { docketClerkCreatesAnIncompleteTrialSessionBeforeCalendaring } from './journey/docketClerkCreatesAnIncompleteTrialSessionBeforeCalendaring'; import { docketClerkSetsCaseReadyForTrial } from './journey/docketClerkSetsCaseReadyForTrial'; import { docketClerkViewsTrialSessionList } from './journey/docketClerkViewsTrialSessionList'; - +import { loginAs, setupTest, uploadPetition } from './helpers'; +import { markAllCasesAsQCed } from './journey/markAllCasesAsQCed'; import { petitionsClerkCompletesAndSetsTrialSession } from './journey/petitionsClerkCompletesAndSetsTrialSession'; import { petitionsClerkSubmitsCaseToIrs } from './journey/petitionsClerkSubmitsCaseToIrs'; import { petitionsClerkViewsDocketRecordAfterSettingTrial } from './journey/petitionsClerkViewsDocketRecordAfterSettingTrial'; @@ -28,14 +28,14 @@ describe('Generate Notices of Trial Session with Paper Service', () => { contactSecondary: { address1: '123 Paper St.', city: 'Paper City', - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, name: 'Richard Papers', phone: '1231231234', postalCode: '12345', state: 'IA', }, hasPaper: true, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, preferredTrialCity: trialLocation, procedureType: 'Small', // should generate a Standing Pretrial Notice trialLocation, diff --git a/web-client/integration-tests/petitionsClerkBlockCase.test.js b/web-client/integration-tests/petitionsClerkBlockCase.test.js index 1327a004862..378aff31052 100644 --- a/web-client/integration-tests/petitionsClerkBlockCase.test.js +++ b/web-client/integration-tests/petitionsClerkBlockCase.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; +import { AUTOMATIC_BLOCKED_REASONS } from '../../shared/src/business/entities/EntityConstants'; import { docketClerkCreatesATrialSession } from './journey/docketClerkCreatesATrialSession'; import { docketClerkSetsCaseReadyForTrial } from './journey/docketClerkSetsCaseReadyForTrial'; import { docketClerkViewsTrialSessionList } from './journey/docketClerkViewsTrialSessionList'; @@ -68,7 +68,7 @@ describe('Blocking a Case', () => { expect(test.getState('blockedCases')).toMatchObject([ { automaticBlocked: true, - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.dueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.dueDate, blocked: false, docketNumber: test.docketNumber, }, @@ -103,7 +103,7 @@ describe('Blocking a Case', () => { expect(test.getState('blockedCases')).toMatchObject([ { automaticBlocked: true, - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.pending, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.pending, blocked: false, docketNumber: test.docketNumber, }, @@ -129,7 +129,7 @@ describe('Blocking a Case', () => { expect(test.getState('blockedCases')).toMatchObject([ { automaticBlocked: true, - automaticBlockedReason: Case.AUTOMATIC_BLOCKED_REASONS.dueDate, + automaticBlockedReason: AUTOMATIC_BLOCKED_REASONS.dueDate, blocked: true, blockedReason: 'just because', docketNumber: test.docketNumber, diff --git a/web-client/integration-tests/petitionsClerkCaseJourney.test.js b/web-client/integration-tests/petitionsClerkCaseJourney.test.js new file mode 100644 index 00000000000..ca2078b5f03 --- /dev/null +++ b/web-client/integration-tests/petitionsClerkCaseJourney.test.js @@ -0,0 +1,58 @@ +import { fakeFile, loginAs, setupTest, uploadPetition } from './helpers'; +import { petitionsClerkAddsDeficiencyStatisticToCase } from './journey/petitionsClerkAddsDeficiencyStatisticToCase'; +import { petitionsClerkAddsOtherStatisticsToCase } from './journey/petitionsClerkAddsOtherStatisticsToCase'; +import { petitionsClerkChangesCaseCaptionDuringQC } from './journey/petitionsClerkChangesCaseCaptionDuringQC'; +import { petitionsClerkCreatesNewCaseFromPaper } from './journey/petitionsClerkCreatesNewCaseFromPaper'; +import { petitionsClerkDeleteDeficiencyStatistic } from './journey/petitionsClerkDeleteDeficiencyStatistic'; +import { petitionsClerkDeletesOtherStatisticToCase } from './journey/petitionsClerkDeletesOtherStatisticToCase'; +import { petitionsClerkEditOtherStatisticToCase } from './journey/petitionsClerkEditOtherStatisticToCase'; +import { petitionsClerkEditsDeficiencyStatistic } from './journey/petitionsClerkEditsDeficiencyStatistic'; +import { petitionsClerkEditsPetitionInQCIRSNotice } from './journey/petitionsClerkEditsPetitionInQCIRSNotice'; +import { petitionsClerkVerifiesOrderDesignatingPlaceOfTrialCheckbox } from './journey/petitionsClerkVerifiesOrderDesignatingPlaceOfTrialCheckbox'; +import { petitionsClerkVerifiesOrderForOdsCheckbox } from './journey/petitionsClerkVerifiesOrderForOdsCheckbox'; +import { petitionsClerkVerifiesPetitionPaymentFeeOptions } from './journey/petitionsClerkVerifiesPetitionPaymentFeeOptions'; + +const test = setupTest(); + +describe('Petitions clerk case journey', () => { + beforeAll(() => { + jest.setTimeout(40000); + }); + + loginAs(test, 'petitionsclerk'); + petitionsClerkCreatesNewCaseFromPaper(test, fakeFile); + petitionsClerkVerifiesOrderForOdsCheckbox(test, fakeFile); + petitionsClerkVerifiesOrderDesignatingPlaceOfTrialCheckbox(test, fakeFile); + petitionsClerkVerifiesPetitionPaymentFeeOptions(test, fakeFile); + + loginAs(test, 'petitioner'); + it('Create case #1', async () => { + const caseDetail = await uploadPetition(test); + expect(caseDetail.docketNumber).toBeDefined(); + test.docketNumber = caseDetail.docketNumber; + test.documentId = caseDetail.documents[0].documentId; + test.caseId = caseDetail.caseId; + }); + + loginAs(test, 'petitionsclerk'); + petitionsClerkEditsPetitionInQCIRSNotice(test); + petitionsClerkChangesCaseCaptionDuringQC(test); + + loginAs(test, 'petitioner'); + it('Create case #2', async () => { + const caseDetail = await uploadPetition(test); + expect(caseDetail.docketNumber).toBeDefined(); + test.docketNumber = caseDetail.docketNumber; + test.documentId = caseDetail.documents[0].documentId; + test.caseId = caseDetail.caseId; + }); + + loginAs(test, 'petitionsclerk'); + petitionsClerkAddsDeficiencyStatisticToCase(test); + petitionsClerkEditsDeficiencyStatistic(test); + petitionsClerkDeleteDeficiencyStatistic(test); + + petitionsClerkAddsOtherStatisticsToCase(test); + petitionsClerkEditOtherStatisticToCase(test); + petitionsClerkDeletesOtherStatisticToCase(test); +}); diff --git a/web-client/integration-tests/petitionsClerkCounselAssociationJourney.test.js b/web-client/integration-tests/petitionsClerkCounselAssociationJourney.test.js index 19f40c34710..f69e42f9cfd 100644 --- a/web-client/integration-tests/petitionsClerkCounselAssociationJourney.test.js +++ b/web-client/integration-tests/petitionsClerkCounselAssociationJourney.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { loginAs, setupTest, uploadPetition } from './helpers'; import { petitionsClerkAddsPractitionersToCase } from './journey/petitionsClerkAddsPractitionersToCase'; import { petitionsClerkAddsRespondentsToCase } from './journey/petitionsClerkAddsRespondentsToCase'; @@ -26,7 +26,7 @@ describe('Petitions Clerk Counsel Association Journey', () => { postalCode: '77546', state: 'AZ', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; diff --git a/web-client/integration-tests/petitionsClerkPaperCaseJourney.test.js b/web-client/integration-tests/petitionsClerkPaperCaseJourney.test.js deleted file mode 100644 index a7ec93de632..00000000000 --- a/web-client/integration-tests/petitionsClerkPaperCaseJourney.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import { fakeFile, loginAs, setupTest, uploadPetition } from './helpers'; -import { petitionsClerkCreatesNewCaseFromPaper } from './journey/petitionsClerkCreatesNewCaseFromPaper'; -// import { petitionsClerkEditsAnExistingCaseAndServesCase } from './journey/petitionsClerkEditsAnExistingCaseAndServesCase'; -import { petitionsClerkEditsPetitionInQCIRSNotice } from './journey/petitionsClerkEditsPetitionInQCIRSNotice'; -import { petitionsClerkVerifiesOrderDesignatingPlaceOfTrialCheckbox } from './journey/petitionsClerkVerifiesOrderDesignatingPlaceOfTrialCheckbox'; -import { petitionsClerkVerifiesOrderForOdsCheckbox } from './journey/petitionsClerkVerifiesOrderForOdsCheckbox'; -import { petitionsClerkVerifiesPetitionPaymentFeeOptions } from './journey/petitionsClerkVerifiesPetitionPaymentFeeOptions'; - -const test = setupTest(); - -describe('Petitions clerk paper case flow', () => { - beforeAll(() => { - jest.setTimeout(40000); - }); - - loginAs(test, 'petitionsclerk'); - petitionsClerkCreatesNewCaseFromPaper(test, fakeFile); - - loginAs(test, 'petitioner'); - it('Create case', async () => { - const caseDetail = await uploadPetition(test); - expect(caseDetail.docketNumber).toBeDefined(); - test.docketNumber = caseDetail.docketNumber; - test.documentId = caseDetail.documents[0].documentId; - test.caseId = caseDetail.caseId; - }); - - loginAs(test, 'petitionsclerk'); - // petitionsClerkEditsAnExistingCaseAndServesCase(test); - petitionsClerkEditsPetitionInQCIRSNotice(test); - petitionsClerkVerifiesOrderForOdsCheckbox(test, fakeFile); - petitionsClerkVerifiesOrderDesignatingPlaceOfTrialCheckbox(test, fakeFile); - petitionsClerkVerifiesPetitionPaymentFeeOptions(test, fakeFile); -}); diff --git a/web-client/integration-tests/practitionerCaseJourney.test.js b/web-client/integration-tests/practitionerCaseJourney.test.js index 1fcb67f8b88..a963bb63db2 100644 --- a/web-client/integration-tests/practitionerCaseJourney.test.js +++ b/web-client/integration-tests/practitionerCaseJourney.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { fakeFile, loginAs, setupTest, uploadPetition } from './helpers'; import { practitionerCreatesNewCase } from './journey/practitionerCreatesNewCase'; import { practitionerFilesDocumentForOwnedCase } from './journey/practitionerFilesDocumentForOwnedCase'; @@ -38,7 +38,7 @@ describe('Practitioner requests access to case', () => { postalCode: '77546', state: 'AZ', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; @@ -68,7 +68,7 @@ describe('Practitioner requests access to case', () => { postalCode: '77546', state: 'AZ', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); expect(caseDetail.docketNumber).toBeDefined(); test.docketNumber = caseDetail.docketNumber; diff --git a/web-client/integration-tests/scanHelpers.js b/web-client/integration-tests/scanHelpers.js index 2ea23b17c9a..dbb27f42789 100644 --- a/web-client/integration-tests/scanHelpers.js +++ b/web-client/integration-tests/scanHelpers.js @@ -1,6 +1,6 @@ import { setBatchPages } from './helpers'; -exports.addBatchesForScanning = ( +export const addBatchesForScanning = ( test, { scannerSourceIndex, scannerSourceName }, ) => { @@ -22,8 +22,7 @@ exports.addBatchesForScanning = ( ]); }); }; - -exports.createPDFFromScannedBatches = test => { +export const createPDFFromScannedBatches = test => { return it('Creates a PDF from added batches', async () => { const selectedDocumentType = test.getState( 'currentViewMetadata.documentSelectedForScan', @@ -43,7 +42,7 @@ exports.createPDFFromScannedBatches = test => { }); }; -exports.selectScannerSource = ( +export const selectScannerSource = ( test, { scannerSourceIndex, scannerSourceName }, ) => { diff --git a/web-client/integration-tests/sentWorkItemsExpireAfter7Days.test.js b/web-client/integration-tests/sentWorkItemsExpireAfter7Days.test.js index 180f4c4af83..2dc7efd7d8a 100644 --- a/web-client/integration-tests/sentWorkItemsExpireAfter7Days.test.js +++ b/web-client/integration-tests/sentWorkItemsExpireAfter7Days.test.js @@ -1,5 +1,7 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; -import { User } from '../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + ROLES, +} from '../../shared/src/business/entities/EntityConstants'; import { getFormattedMyOutbox, getFormattedSectionOutbox, @@ -32,7 +34,7 @@ describe('verify old sent work items do not show up in the outbox', () => { expect(caseDetail.docketNumber).toBeDefined(); const applicationContext = applicationContextFactory({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: '3805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -52,7 +54,7 @@ describe('verify old sent work items do not show up in the outbox', () => { assigneeId: '3805d1ab-18d0-43ec-bafb-654e83405416', assigneeName: 'Test petitionsclerk1', caseId: 'd481929a-fb22-4800-900e-50b15ac55934', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, createdAt: CREATED_8_DAYS_AGO.toISOString(), docketNumber: caseDetail.docketNumber, docketNumberSuffix: null, diff --git a/web-client/integration-tests/servedWorkItemsExpireAfter7Days.test.js b/web-client/integration-tests/servedWorkItemsExpireAfter7Days.test.js index 7b9f9384eba..7434e7ed4e1 100644 --- a/web-client/integration-tests/servedWorkItemsExpireAfter7Days.test.js +++ b/web-client/integration-tests/servedWorkItemsExpireAfter7Days.test.js @@ -1,5 +1,7 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; -import { User } from '../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + ROLES, +} from '../../shared/src/business/entities/EntityConstants'; import { getFormattedDocumentQCMyOutbox, getFormattedDocumentQCSectionOutbox, @@ -33,7 +35,7 @@ describe('verify old served work items do not show up in the outbox', () => { expect(caseDetail.docketNumber).toBeDefined(); const applicationContext = applicationContextFactory({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: '3805d1ab-18d0-43ec-bafb-654e83405416', }); @@ -53,7 +55,7 @@ describe('verify old served work items do not show up in the outbox', () => { assigneeId: '3805d1ab-18d0-43ec-bafb-654e83405416', assigneeName: 'Test petitionsclerk1', caseId: 'd481929a-fb22-4800-900e-50b15ac55934', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, completedAt: '2019-06-26T16:31:17.643Z', completedByUserId: '3805d1ab-18d0-43ec-bafb-654e83405416', createdAt: CREATED_8_DAYS_AGO.toISOString(), @@ -77,7 +79,7 @@ describe('verify old served work items do not show up in the outbox', () => { toUserId: '3805d1ab-18d0-43ec-bafb-654e83405416', }, ], - section: 'irsBatchSection', + section: 'irsSystem', sentBy: 'Test petitionsclerk1', sentBySection: 'petitions', sentByUserId: '3805d1ab-18d0-43ec-bafb-654e83405416', diff --git a/web-client/integration-tests/signAndServeStipulatedDecision.test.js b/web-client/integration-tests/signAndServeStipulatedDecision.test.js index 20a29d7d167..99234f3bb69 100644 --- a/web-client/integration-tests/signAndServeStipulatedDecision.test.js +++ b/web-client/integration-tests/signAndServeStipulatedDecision.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../shared/src/business/entities/EntityConstants'; import { createCourtIssuedDocketEntry, @@ -134,6 +134,6 @@ describe('a user signs and serves a stipulated decision', () => { d => d.documentId === signedDocumentId, ); expect(signedDocument.servedAt).toBeDefined(); - expect(caseDetail.status).toEqual(Case.STATUS_TYPES.closed); + expect(caseDetail.status).toEqual(CASE_STATUS_TYPES.closed); }); }); diff --git a/web-client/integration-tests/trialSessionEligibleCasesJourney.test.js b/web-client/integration-tests/trialSessionEligibleCasesJourney.test.js index 649a8bdbfde..b7ebcc6ea4d 100644 --- a/web-client/integration-tests/trialSessionEligibleCasesJourney.test.js +++ b/web-client/integration-tests/trialSessionEligibleCasesJourney.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../shared/src/business/entities/cases/Case'; +import { CHIEF_JUDGE } from '../../shared/src/business/entities/EntityConstants'; import { docketClerkCreatesATrialSession } from './journey/docketClerkCreatesATrialSession'; import { docketClerkSetsCaseReadyForTrial } from './journey/docketClerkSetsCaseReadyForTrial'; import { docketClerkViewsNewTrialSession } from './journey/docketClerkViewsNewTrialSession'; @@ -294,9 +294,7 @@ describe('Trial Session Eligible Cases Journey', () => { expect(test.getState('caseDetail.status')).not.toEqual('Calendared'); expect(test.getState('caseDetail.trialLocation')).toBeUndefined(); expect(test.getState('caseDetail.trialDate')).toBeUndefined(); - expect(test.getState('caseDetail.associatedJudge')).toEqual( - Case.CHIEF_JUDGE, - ); + expect(test.getState('caseDetail.associatedJudge')).toEqual(CHIEF_JUDGE); //Case #3 - not assigned await test.runSequence('gotoCaseDetailSequence', { diff --git a/web-client/jest-integration.config.js b/web-client/jest-integration.config.js index b65941b5b82..e8a043d71a0 100644 --- a/web-client/jest-integration.config.js +++ b/web-client/jest-integration.config.js @@ -19,10 +19,10 @@ module.exports = { coverageDirectory: './coverage-integration', coverageThreshold: { global: { - branches: 30, - functions: 30, - lines: 30, - statements: 30, + branches: 20, + functions: 20, + lines: 20, + statements: 20, }, }, globals: { diff --git a/web-client/pa11y/pa11y-docketclerk.js b/web-client/pa11y/pa11y-docketclerk.js index 96ff7cc603f..8481318c26c 100644 --- a/web-client/pa11y/pa11y-docketclerk.js +++ b/web-client/pa11y/pa11y-docketclerk.js @@ -21,7 +21,7 @@ module.exports = [ notes: 'checks a11y of form when petition fee payment status paid is selected', url: - 'http://localhost:1234/mock-login?token=docketclerk&path=/case-detail/105-19/edit-details&info=paid', + 'http://localhost:1234/mock-login?token=docketclerk&path=/case-detail/101-19/edit-details&info=paid', }, { actions: [ @@ -32,7 +32,7 @@ module.exports = [ notes: 'checks a11y of form when petition fee payment status unpaid is selected', url: - 'http://localhost:1234/mock-login?token=docketclerk&path=/case-detail/105-19/edit-details&info=unpaid', + 'http://localhost:1234/mock-login?token=docketclerk&path=/case-detail/101-19/edit-details&info=unpaid', }, { actions: [ @@ -43,7 +43,7 @@ module.exports = [ notes: 'checks a11y of form when petition fee payment status waived is selected', url: - 'http://localhost:1234/mock-login?token=docketclerk&path=/case-detail/105-19/edit-details&info=waived', + 'http://localhost:1234/mock-login?token=docketclerk&path=/case-detail/101-19/edit-details&info=waived', }, 'http://localhost:1234/mock-login?token=docketclerk&path=/case-detail/101-19/documents/1f1aa3f7-e2e3-43e6-885d-4ce341588c76', { diff --git a/web-client/pa11y/pa11y-petitionsclerk.js b/web-client/pa11y/pa11y-petitionsclerk.js index ffaf1f3b591..92e37516d44 100644 --- a/web-client/pa11y/pa11y-petitionsclerk.js +++ b/web-client/pa11y/pa11y-petitionsclerk.js @@ -152,6 +152,20 @@ module.exports = [ url: 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/101-19&info=edit-signed-order-confirm-modal', }, + { + actions: [ + 'wait for #case-detail-menu-button to be visible', + 'wait for .progress-indicator to be hidden', + 'click element #case-detail-menu-button', + 'wait for #menu-button-add-new-message to be visible', + 'wait for .progress-indicator to be hidden', + 'click element #menu-button-add-new-message', + 'wait for .ustc-create-message-modal to be visible', + ], + notes: 'checks a11y of create message modal', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/105-19&info=create-message-modal', + }, { actions: [ 'wait for #case-detail-menu-button to be visible', @@ -231,6 +245,34 @@ module.exports = [ url: 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/109-19&info=in-progress-tab-pending-report', }, + { + actions: [ + 'wait for #tab-correspondence to be visible', + 'click element #tab-correspondence', + 'wait for #correspondence-documents-table to be visible', + ], + notes: 'checks a11y of correspondence tab', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/103-19&info=correspondence-tab', + }, + { + actions: ['wait for element #upload-correspondence to be visible'], + notes: 'checks a11y of add correspondence page', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/103-19/upload-correspondence&info=add-correspondence', + }, + { + actions: [ + 'wait for #tab-correspondence to be visible', + 'click element #tab-correspondence', + 'wait for #correspondence-documents-table to be visible', + 'click element .edit-correspondence-button', + 'wait for element #edit-correspondence-header to be visible', + ], + notes: 'checks a11y of edit correspondence page', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/103-19&info=edit-correspondence', + }, { actions: [ 'wait for #tab-case-information to be visible', @@ -279,6 +321,42 @@ module.exports = [ url: 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/104-19&info=add-case-to-session-modal', }, + { + actions: [ + 'wait for #tab-case-information to be visible', + 'click element #tab-case-information', + 'wait for #tab-statistics to be visible', + 'click element #tab-statistics', + 'wait for #tabContent-statistics to be visible', + ], + notes: 'checks the case detail => case information => statistics tab', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/105-20&info=statistics', + }, + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/101-19/add-other-statistics', + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/105-20/add-deficiency-statistics', + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/101-19/edit-other-statistics', + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/105-20/edit-deficiency-statistic/cb557361-50ee-4440-aaff-0a9f1bfa30ed', + { + actions: [ + 'wait for button.red-warning to be visible', + 'click element button.red-warning', + 'wait for #modal-root to be visible', + ], + notes: 'checks the delete deficiency modal', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/105-20/edit-deficiency-statistic/cb557361-50ee-4440-aaff-0a9f1bfa30ed&info=delete-deficiency-modal', + }, + { + actions: [ + 'wait for button.red-warning to be visible', + 'click element button.red-warning', + 'wait for #modal-root to be visible', + ], + notes: 'checks the delete modal', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/101-19/edit-other-statistics&info=delete-other-statistics-modal', + }, /* petition qc */ 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/104-19/petition-qc?tab=partyInfo', @@ -295,6 +373,38 @@ module.exports = [ url: 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/102-19/petition-qc?tab=irsNotice&info=reveal-notice-options', }, + { + actions: [ + 'wait for #tab-irs-notice to be visible', + 'click element #tab-irs-notice', + 'wait for #irs-verified-notice-radios to be visible', + 'click element #has-irs-verified-notice-yes', + 'wait for #date-of-notice-legend to be visible', + 'set field #case-type to Deficiency', + 'check field #case-type', + 'wait for .statistic-form to be visible', + ], + notes: 'checks the statistics section of the petition QC screen', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/file-a-petition/step-1&info=statistics-petition-qc', + }, + { + actions: [ + 'wait for #tab-irs-notice to be visible', + 'click element #tab-irs-notice', + 'wait for #irs-verified-notice-radios to be visible', + 'click element #has-irs-verified-notice-yes', + 'wait for #date-of-notice-legend to be visible', + 'set field #case-type to Deficiency', + 'check field #case-type', + 'wait for .calculate-penalties to be visible', + 'click element .calculate-penalties', + 'wait for .modal-screen to be visible', + ], + notes: 'checks the Calculate Penalties on IRS Notice modal', + url: + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/file-a-petition/step-1&info=penalties-modal', + }, /* review petition */ 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-detail/104-19/documents/c63be3f2-2240-451e-b6bd-8206d52a070b/review', @@ -372,21 +482,10 @@ module.exports = [ 'http://localhost:1234/mock-login?token=petitionsclerk&path=/search&info=practitioner-search-results', }, 'http://localhost:1234/mock-login?token=petitionsclerk&path=/practitioner-detail/PT1234', - { - actions: [ - 'wait for #tab-irs-notice to be visible', - 'click element #tab-irs-notice', - 'wait for #irs-verified-notice-radios to be visible', - 'click element #has-irs-verified-notice-yes', - 'wait for #date-of-notice-legend to be visible', - 'set field #case-type to Deficiency', - 'check field #case-type', - 'wait for .calculate-penalties to be visible', - 'click element .calculate-penalties', - 'wait for .modal-screen to be visible', - ], - notes: 'checks the Calculate Penalties on IRS Notice modal', - url: - 'http://localhost:1234/mock-login?token=petitionsclerk&path=/file-a-petition/step-1&info=penalties-modal', - }, + /* case messages */ + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-messages/my/inbox&info=case-messages-inbox', + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-messages/my/outbox&info=case-messages-outbox', + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-messages/section/inbox&info=case-messages-section-inbox', + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-messages/section/outbox&info=case-messages-section-outbox', + 'http://localhost:1234/mock-login?token=petitionsclerk&path=/case-messages/105-20/message-detail/eb0a139a-8951-4de1-8b83-f02a27504105&info=case-message-detail', ]; diff --git a/web-client/pa11y/pa11y-private-practitioner.js b/web-client/pa11y/pa11y-private-practitioner.js index d7e3a89d487..30ec024adb0 100644 --- a/web-client/pa11y/pa11y-private-practitioner.js +++ b/web-client/pa11y/pa11y-private-practitioner.js @@ -5,4 +5,13 @@ module.exports = [ 'http://localhost:1234/mock-login?token=privatePractitioner&path=/file-a-petition/step-1', 'http://localhost:1234/mock-login?token=privatePractitioner&path=/case-detail/102-19/request-access', 'http://localhost:1234/mock-login?token=privatePractitioner&path=/search/no-matches', + { + actions: [ + 'wait for #tab-closed to be visible', + 'click element #tab-closed', + 'wait for element #tabContent-closed to be visible', + ], + notes: 'check the a11y of the Closed Cases tab', + url: 'http://localhost:1234/mock-login?token=privatePractitioner&path=/', + }, ]; diff --git a/web-client/pa11y/pa11y-public-user.js b/web-client/pa11y/pa11y-public-user.js index a68dde8fc5c..9fb8e4990db 100644 --- a/web-client/pa11y/pa11y-public-user.js +++ b/web-client/pa11y/pa11y-public-user.js @@ -1,4 +1,14 @@ module.exports = [ + { + actions: [ + 'wait for #tab-case to be visible', + 'set field #docket-number to 103-20', + 'click element button#docket-search-button', + 'wait for table.docket-record to be visible', + ], + notes: 'checks a11y of advanced case search', + url: 'http://localhost:5678/', + }, { actions: [ 'wait for #tab-order to be visible', @@ -9,7 +19,7 @@ module.exports = [ 'wait for table.search-results to be visible', ], notes: 'checks a11y of advanced order search', - url: 'http://localhost:5678', + url: 'http://localhost:5678/', }, { actions: [ @@ -21,7 +31,7 @@ module.exports = [ 'wait for #no-search-results to be visible', ], notes: 'checks a11y of advanced order search with no results', - url: 'http://localhost:5678?info=no-results', + url: 'http://localhost:5678/', }, { actions: [ @@ -33,6 +43,7 @@ module.exports = [ 'wait for table.search-results to be visible', ], notes: 'checks a11y of advanced opinion search with results', - url: 'http://localhost:5678?info=no-results', + url: 'http://localhost:5678/', }, + 'http://localhost:5678/todays-opinions', ]; diff --git a/web-client/src/app.jsx b/web-client/src/app.jsx index b2a49490cec..7971f9a9ea3 100644 --- a/web-client/src/app.jsx +++ b/web-client/src/app.jsx @@ -10,83 +10,82 @@ import { route, router, } from './router'; -import { - faArrowAltCircleLeft as faArrowAltCircleLeftRegular, - faCheckCircle as faCheckCircleRegular, - faClock, - faClone, - faCopy, - faEdit, - faEyeSlash, - faFileAlt, - faFilePdf as faFilePdfRegular, - faTimesCircle as faTimesCircleRegular, - faUser, -} from '@fortawesome/free-regular-svg-icons'; -import { - faArrowAltCircleLeft as faArrowAltCircleLeftSolid, - faCalculator, - faCalendarAlt, - faCalendarCheck, - faCalendarPlus, - faCaretDown, - faCaretLeft, - faCaretRight, - faCaretUp, - faCheck, - faCheckCircle, - faClipboardList, - faClock as faClockSolid, - faCloudDownloadAlt, - faCloudUploadAlt, - faCopy as faCopySolid, - faDollarSign, - faEdit as faEditSolid, - faEnvelopeOpen, - faEnvelope as faEnvelopeSolid, - faExclamation, - faExclamationCircle, - faExclamationTriangle, - faFile, - faFileAlt as faFileAltSolid, - faFilePdf, - faFingerprint, - faFlag, - faGavel, - faHandPaper, - faHome, - faInfoCircle, - faLaptop, - faLink, - faListUl, - faLock, - faMinus, - faMinusCircle, - faPaperPlane, - faPaperclip, - faPencilAlt, - faPlus, - faPlusCircle, - faPrint, - faQuestionCircle, - faRedoAlt, - faSearch, - faShareSquare, - faShieldAlt, - faSignOutAlt, - faSlash, - faSort, - faSpinner, - faStar, - faStepBackward, - faStepForward, - faStickyNote, - faSync, - faThumbtack, - faTimesCircle, - faTrash, - faUserCheck, -} from '@fortawesome/free-solid-svg-icons'; + +import { faArrowAltCircleLeft as faArrowAltCircleLeftRegular } from '@fortawesome/free-regular-svg-icons/faArrowAltCircleLeft'; +import { faCheckCircle as faCheckCircleRegular } from '@fortawesome/free-regular-svg-icons/faCheckCircle'; +import { faClock } from '@fortawesome/free-regular-svg-icons/faClock'; +import { faClone } from '@fortawesome/free-regular-svg-icons/faClone'; +import { faCopy } from '@fortawesome/free-regular-svg-icons/faCopy'; +import { faEdit } from '@fortawesome/free-regular-svg-icons/faEdit'; +import { faEyeSlash } from '@fortawesome/free-regular-svg-icons/faEyeSlash'; +import { faFileAlt } from '@fortawesome/free-regular-svg-icons/faFileAlt'; +import { faFilePdf as faFilePdfRegular } from '@fortawesome/free-regular-svg-icons/faFilePdf'; +import { faTimesCircle as faTimesCircleRegular } from '@fortawesome/free-regular-svg-icons/faTimesCircle'; +import { faUser } from '@fortawesome/free-regular-svg-icons/faUser'; + +import { faArrowAltCircleLeft as faArrowAltCircleLeftSolid } from '@fortawesome/free-solid-svg-icons/faArrowAltCircleLeft'; +import { faCalculator } from '@fortawesome/free-solid-svg-icons/faCalculator'; +import { faCalendarAlt } from '@fortawesome/free-solid-svg-icons/faCalendarAlt'; +import { faCalendarCheck } from '@fortawesome/free-solid-svg-icons/faCalendarCheck'; +import { faCalendarPlus } from '@fortawesome/free-solid-svg-icons/faCalendarPlus'; +import { faCaretDown } from '@fortawesome/free-solid-svg-icons/faCaretDown'; +import { faCaretLeft } from '@fortawesome/free-solid-svg-icons/faCaretLeft'; +import { faCaretRight } from '@fortawesome/free-solid-svg-icons/faCaretRight'; +import { faCaretUp } from '@fortawesome/free-solid-svg-icons/faCaretUp'; +import { faCheck } from '@fortawesome/free-solid-svg-icons/faCheck'; +import { faCheckCircle } from '@fortawesome/free-solid-svg-icons/faCheckCircle'; +import { faClipboardList } from '@fortawesome/free-solid-svg-icons/faClipboardList'; +import { faClock as faClockSolid } from '@fortawesome/free-solid-svg-icons/faClock'; +import { faCloudDownloadAlt } from '@fortawesome/free-solid-svg-icons/faCloudDownloadAlt'; +import { faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons/faCloudUploadAlt'; +import { faCopy as faCopySolid } from '@fortawesome/free-solid-svg-icons/faCopy'; +import { faDollarSign } from '@fortawesome/free-solid-svg-icons/faDollarSign'; +import { faEdit as faEditSolid } from '@fortawesome/free-solid-svg-icons/faEdit'; +import { faEnvelopeOpen } from '@fortawesome/free-solid-svg-icons/faEnvelopeOpen'; +import { faEnvelope as faEnvelopeSolid } from '@fortawesome/free-solid-svg-icons/faEnvelope'; +import { faExclamation } from '@fortawesome/free-solid-svg-icons/faExclamation'; +import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons/faExclamationCircle'; +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons/faExclamationTriangle'; +import { faFile } from '@fortawesome/free-solid-svg-icons/faFile'; +import { faFileAlt as faFileAltSolid } from '@fortawesome/free-solid-svg-icons/faFileAlt'; +import { faFilePdf } from '@fortawesome/free-solid-svg-icons/faFilePdf'; +import { faFingerprint } from '@fortawesome/free-solid-svg-icons/faFingerprint'; +import { faFlag } from '@fortawesome/free-solid-svg-icons/faFlag'; +import { faGavel } from '@fortawesome/free-solid-svg-icons/faGavel'; +import { faHandPaper } from '@fortawesome/free-solid-svg-icons/faHandPaper'; +import { faHome } from '@fortawesome/free-solid-svg-icons/faHome'; +import { faInfoCircle } from '@fortawesome/free-solid-svg-icons/faInfoCircle'; +import { faLaptop } from '@fortawesome/free-solid-svg-icons/faLaptop'; +import { faLink } from '@fortawesome/free-solid-svg-icons/faLink'; +import { faListUl } from '@fortawesome/free-solid-svg-icons/faListUl'; +import { faLock } from '@fortawesome/free-solid-svg-icons/faLock'; +import { faMinus } from '@fortawesome/free-solid-svg-icons/faMinus'; +import { faMinusCircle } from '@fortawesome/free-solid-svg-icons/faMinusCircle'; +import { faPaperPlane } from '@fortawesome/free-solid-svg-icons/faPaperPlane'; +import { faPaperclip } from '@fortawesome/free-solid-svg-icons/faPaperclip'; +import { faPencilAlt } from '@fortawesome/free-solid-svg-icons/faPencilAlt'; +import { faPlus } from '@fortawesome/free-solid-svg-icons/faPlus'; +import { faPlusCircle } from '@fortawesome/free-solid-svg-icons/faPlusCircle'; +import { faPrint } from '@fortawesome/free-solid-svg-icons/faPrint'; +import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons/faQuestionCircle'; +import { faRedoAlt } from '@fortawesome/free-solid-svg-icons/faRedoAlt'; +import { faSearch } from '@fortawesome/free-solid-svg-icons/faSearch'; +import { faShareSquare } from '@fortawesome/free-solid-svg-icons/faShareSquare'; +import { faShieldAlt } from '@fortawesome/free-solid-svg-icons/faShieldAlt'; +import { faSignOutAlt } from '@fortawesome/free-solid-svg-icons/faSignOutAlt'; +import { faSlash } from '@fortawesome/free-solid-svg-icons/faSlash'; +import { faSort } from '@fortawesome/free-solid-svg-icons/faSort'; +import { faSpinner } from '@fortawesome/free-solid-svg-icons/faSpinner'; +import { faStar } from '@fortawesome/free-solid-svg-icons/faStar'; +import { faStepBackward } from '@fortawesome/free-solid-svg-icons/faStepBackward'; +import { faStepForward } from '@fortawesome/free-solid-svg-icons/faStepForward'; +import { faStickyNote } from '@fortawesome/free-solid-svg-icons/faStickyNote'; +import { faSync } from '@fortawesome/free-solid-svg-icons/faSync'; +import { faThumbtack } from '@fortawesome/free-solid-svg-icons/faThumbtack'; +import { faTimesCircle } from '@fortawesome/free-solid-svg-icons/faTimesCircle'; +import { faTrash } from '@fortawesome/free-solid-svg-icons/faTrash'; +import { faUserCheck } from '@fortawesome/free-solid-svg-icons/faUserCheck'; + import { isFunction, mapValues } from 'lodash'; import { library } from '@fortawesome/fontawesome-svg-core'; import { presenter } from './presenter/presenter'; diff --git a/web-client/src/applicationContext.js b/web-client/src/applicationContext.js index 158d8a2a442..315ad0c05af 100644 --- a/web-client/src/applicationContext.js +++ b/web-client/src/applicationContext.js @@ -29,6 +29,7 @@ import { User } from '../../shared/src/business/entities/User'; import { addCaseToTrialSessionInteractor } from '../../shared/src/proxies/trialSessions/addCaseToTrialSessionProxy'; import { addConsolidatedCaseInteractor } from '../../shared/src/proxies/addConsolidatedCaseProxy'; import { addCoversheetInteractor } from '../../shared/src/proxies/documents/addCoversheetProxy'; +import { addDeficiencyStatisticInteractor } from '../../shared/src/proxies/caseStatistics/addDeficiencyStatisticProxy'; import { archiveDraftDocumentInteractor } from '../../shared/src/proxies/archiveDraftDocumentProxy'; import { assignWorkItemsInteractor } from '../../shared/src/proxies/workitems/assignWorkItemsProxy'; import { associateIrsPractitionerWithCaseInteractor } from '../../shared/src/proxies/manualAssociation/associateIrsPractitionerWithCaseProxy'; @@ -36,6 +37,18 @@ import { associatePrivatePractitionerWithCaseInteractor } from '../../shared/src import { authorizeCodeInteractor } from '../../shared/src/business/useCases/authorizeCodeInteractor'; import { batchDownloadTrialSessionInteractor } from '../../shared/src/proxies/trialSessions/batchDownloadTrialSessionProxy'; import { blockCaseFromTrialInteractor } from '../../shared/src/proxies/blockCaseFromTrialProxy'; +import { + calculateISODate, + createISODateString, + createISODateStringFromObject, + dateStringsCompared, + deconstructDate, + formatDateString, + formatNow, + isStringISOFormatted, + isValidDateString, + prepareDateFromString, +} from '../../shared/src/business/utilities/DateHandler'; import { canConsolidateInteractor } from '../../shared/src/business/useCases/caseConsolidation/canConsolidateInteractor'; import { canSetTrialSessionAsCalendaredInteractor } from '../../shared/src/business/useCases/trialSessions/canSetTrialSessionAsCalendaredInteractor'; import { caseAdvancedSearchInteractor } from '../../shared/src/proxies/caseAdvancedSearchProxy'; @@ -50,26 +63,19 @@ import { completeWorkItemInteractor } from '../../shared/src/proxies/workitems/c import { createCaseDeadlineInteractor } from '../../shared/src/proxies/caseDeadline/createCaseDeadlineProxy'; import { createCaseFromPaperInteractor } from '../../shared/src/proxies/createCaseFromPaperProxy'; import { createCaseInteractor } from '../../shared/src/proxies/createCaseProxy'; +import { createCaseMessageInteractor } from '../../shared/src/proxies/messages/createCaseMessageProxy'; import { createCourtIssuedOrderPdfFromHtmlInteractor } from '../../shared/src/proxies/courtIssuedOrder/createCourtIssuedOrderPdfFromHtmlProxy'; -import { - createISODateString, - createISODateStringFromObject, - deconstructDate, - formatDateString, - formatNow, - isStringISOFormatted, - isValidDateString, - prepareDateFromString, -} from '../../shared/src/business/utilities/DateHandler'; import { createPractitionerUserInteractor } from '../../shared/src/proxies/practitioners/createPractitionerUserProxy'; import { createTrialSessionInteractor } from '../../shared/src/proxies/trialSessions/createTrialSessionProxy'; import { createWorkItemInteractor } from '../../shared/src/proxies/workitems/createWorkItemProxy'; import { deleteCaseDeadlineInteractor } from '../../shared/src/proxies/caseDeadline/deleteCaseDeadlineProxy'; import { deleteCaseNoteInteractor } from '../../shared/src/proxies/caseNote/deleteCaseNoteProxy'; +import { deleteCorrespondenceDocumentInteractor } from '../../shared/src/proxies/correspondence/deleteCorrespondenceDocumentProxy'; import { deleteCounselFromCaseInteractor } from '../../shared/src/proxies/caseAssociation/deleteCounselFromCaseProxy'; +import { deleteDeficiencyStatisticInteractor } from '../../shared/src/proxies/caseStatistics/deleteDeficiencyStatisticProxy'; import { deleteTrialSessionInteractor } from '../../shared/src/proxies/trialSessions/deleteTrialSessionProxy'; import { deleteUserCaseNoteInteractor } from '../../shared/src/proxies/caseNote/deleteUserCaseNoteProxy'; -import { fileCorrespondenceDocumentInteractor } from '../../shared/src/proxies/correspondence/fileCorrespondenceDocumentInteractor'; +import { fileCorrespondenceDocumentInteractor } from '../../shared/src/proxies/correspondence/fileCorrespondenceDocumentProxy'; import { fileCourtIssuedDocketEntryInteractor } from '../../shared/src/proxies/documents/fileCourtIssuedDocketEntryProxy'; import { fileCourtIssuedOrderInteractor } from '../../shared/src/proxies/courtIssuedOrder/fileCourtIssuedOrderProxy'; import { fileDocketEntryInteractor } from '../../shared/src/proxies/documents/fileDocketEntryProxy'; @@ -86,7 +92,6 @@ import { getFormattedCaseDetail, sortDocketRecords, } from '../../shared/src/business/utilities/getFormattedCaseDetail'; - import { forwardWorkItemInteractor } from '../../shared/src/proxies/workitems/forwardWorkItemProxy'; import { generateCaseAssociationDocumentTitleInteractor } from '../../shared/src/business/useCases/caseAssociationRequest/generateCaseAssociationDocumentTitleInteractor'; import { generateCourtIssuedDocumentTitleInteractor } from '../../shared/src/business/useCases/courtIssuedDocument/generateCourtIssuedDocumentTitleInteractor'; @@ -102,15 +107,18 @@ import { getCalendaredCasesForTrialSessionInteractor } from '../../shared/src/pr import { getCaseDeadlinesForCaseInteractor } from '../../shared/src/proxies/caseDeadline/getCaseDeadlinesForCaseProxy'; import { getCaseInteractor } from '../../shared/src/proxies/getCaseProxy'; import { getCaseInventoryReportInteractor } from '../../shared/src/proxies/reports/getCaseInventoryReportProxy'; +import { getCaseMessageInteractor } from '../../shared/src/proxies/messages/getCaseMessageProxy'; import { getCasesByUserInteractor } from '../../shared/src/proxies/getCasesByUserProxy'; +import { getClosedCasesInteractor } from '../../shared/src/proxies/getClosedCasesProxy'; import { getConsolidatedCasesByCaseInteractor } from '../../shared/src/proxies/getConsolidatedCasesByCaseProxy'; -import { getConsolidatedCasesByUserInteractor } from '../../shared/src/proxies/getConsolidatedCasesByUserProxy'; import { getDocument } from '../../shared/src/persistence/s3/getDocument'; import { getDocumentQCInboxForSectionInteractor } from '../../shared/src/proxies/workitems/getDocumentQCInboxForSectionProxy'; import { getDocumentQCInboxForUserInteractor } from '../../shared/src/proxies/workitems/getDocumentQCInboxForUserProxy'; import { getDocumentQCServedForSectionInteractor } from '../../shared/src/proxies/workitems/getDocumentQCServedForSectionProxy'; import { getDocumentQCServedForUserInteractor } from '../../shared/src/proxies/workitems/getDocumentQCServedForUserProxy'; import { getEligibleCasesForTrialSessionInteractor } from '../../shared/src/proxies/trialSessions/getEligibleCasesForTrialSessionProxy'; +import { getInboxCaseMessagesForSectionInteractor } from '../../shared/src/proxies/messages/getInboxCaseMessagesForSectionProxy'; +import { getInboxCaseMessagesForUserInteractor } from '../../shared/src/proxies/messages/getInboxCaseMessagesForUserProxy'; import { getInboxMessagesForSectionInteractor } from '../../shared/src/proxies/workitems/getInboxMessagesForSectionProxy'; import { getInboxMessagesForUserInteractor } from '../../shared/src/proxies/workitems/getInboxMessagesForUserProxy'; import { getInternalUsersInteractor } from '../../shared/src/proxies/users/getInternalUsersProxy'; @@ -118,6 +126,9 @@ import { getIrsPractitionersBySearchKeyInteractor } from '../../shared/src/proxi import { getItem } from '../../shared/src/persistence/localStorage/getItem'; import { getItemInteractor } from '../../shared/src/business/useCases/getItemInteractor'; import { getNotificationsInteractor } from '../../shared/src/proxies/users/getNotificationsProxy'; +import { getOpenConsolidatedCasesInteractor } from '../../shared/src/proxies/getOpenConsolidatedCasesProxy'; +import { getOutboxCaseMessagesForSectionInteractor } from '../../shared/src/proxies/messages/getOutboxCaseMessagesForSectionProxy'; +import { getOutboxCaseMessagesForUserInteractor } from '../../shared/src/proxies/messages/getOutboxCaseMessagesForUserProxy'; import { getPdfFromUrl } from '../../shared/src/persistence/s3/getPdfFromUrl'; import { getPdfFromUrlInteractor } from '../../shared/src/business/useCases/document/getPdfFromUrlInteractor'; import { getPractitionerByBarNumberInteractor } from '../../shared/src/proxies/users/getPractitionerByBarNumberProxy'; @@ -171,8 +182,10 @@ import { updateCorrespondenceDocumentInteractor } from '../../shared/src/proxies import { updateCounselOnCaseInteractor } from '../../shared/src/proxies/caseAssociation/updateCounselOnCaseProxy'; import { updateCourtIssuedDocketEntryInteractor } from '../../shared/src/proxies/documents/updateCourtIssuedDocketEntryProxy'; import { updateCourtIssuedOrderInteractor } from '../../shared/src/proxies/courtIssuedOrder/updateCourtIssuedOrderProxy'; +import { updateDeficiencyStatisticInteractor } from '../../shared/src/proxies/caseStatistics/updateDeficiencyStatisticProxy'; import { updateDocketEntryInteractor } from '../../shared/src/proxies/documents/updateDocketEntryProxy'; import { updateDocketEntryMetaInteractor } from '../../shared/src/proxies/documents/updateDocketEntryMetaProxy'; +import { updateOtherStatisticsInteractor } from '../../shared/src/proxies/caseStatistics/updateOtherStatisticsProxy'; import { updatePetitionDetailsInteractor } from '../../shared/src/proxies/updatePetitionDetailsProxy'; import { updatePetitionerInformationInteractor } from '../../shared/src/proxies/updatePetitionerInformationProxy'; import { updatePractitionerUserInteractor } from '../../shared/src/proxies/practitioners/updatePractitionerUserProxy'; @@ -189,6 +202,7 @@ import { uploadDocumentInteractor } from '../../shared/src/business/useCases/ext import { uploadExternalDocumentsInteractor } from '../../shared/src/business/useCases/externalDocument/uploadExternalDocumentsInteractor'; import { uploadOrderDocumentInteractor } from '../../shared/src/business/useCases/externalDocument/uploadOrderDocumentInteractor'; import { uploadPdfFromClient } from '../../shared/src/persistence/s3/uploadPdfFromClient'; +import { validateAddDeficiencyStatisticsInteractor } from '../../shared/src/business/useCases/validateAddDeficiencyStatisticsInteractor'; import { validateAddIrsPractitionerInteractor } from '../../shared/src/business/useCases/caseAssociation/validateAddIrsPractitionerInteractor'; import { validateAddPractitionerInteractor } from '../../shared/src/business/useCases/practitioners/validateAddPractitionerInteractor'; import { validateAddPrivatePractitionerInteractor } from '../../shared/src/business/useCases/caseAssociation/validateAddPrivatePractitionerInteractor'; @@ -197,6 +211,7 @@ import { validateCaseAssociationRequestInteractor } from '../../shared/src/busin import { validateCaseDeadlineInteractor } from '../../shared/src/business/useCases/caseDeadline/validateCaseDeadlineInteractor'; import { validateCaseDetailInteractor } from '../../shared/src/business/useCases/validateCaseDetailInteractor'; import { validateCourtIssuedDocketEntryInteractor } from '../../shared/src/business/useCases/courtIssuedDocument/validateCourtIssuedDocketEntryInteractor'; +import { validateCreateCaseMessageInteractor } from '../../shared/src/business/useCases/messages/validateCreateCaseMessageInteractor'; import { validateDocketEntryInteractor } from '../../shared/src/business/useCases/docketEntry/validateDocketEntryInteractor'; import { validateEditPrivatePractitionerInteractor } from '../../shared/src/business/useCases/caseAssociation/validateEditPrivatePractitionerInteractor'; import { validateExternalDocumentInformationInteractor } from '../../shared/src/business/useCases/externalDocument/validateExternalDocumentInformationInteractor'; @@ -244,6 +259,7 @@ const allUseCases = { addCaseToTrialSessionInteractor, addConsolidatedCaseInteractor, addCoversheetInteractor, + addDeficiencyStatisticInteractor, archiveDraftDocumentInteractor, assignWorkItemsInteractor, associateIrsPractitionerWithCaseInteractor, @@ -259,13 +275,16 @@ const allUseCases = { createCaseDeadlineInteractor, createCaseFromPaperInteractor, createCaseInteractor, + createCaseMessageInteractor, createCourtIssuedOrderPdfFromHtmlInteractor, createPractitionerUserInteractor, createTrialSessionInteractor, createWorkItemInteractor, deleteCaseDeadlineInteractor, deleteCaseNoteInteractor, + deleteCorrespondenceDocumentInteractor, deleteCounselFromCaseInteractor, + deleteDeficiencyStatisticInteractor, deleteTrialSessionInteractor, deleteUserCaseNoteInteractor, fetchPendingItemsInteractor, @@ -294,14 +313,17 @@ const allUseCases = { getCaseDeadlinesForCaseInteractor, getCaseInteractor, getCaseInventoryReportInteractor, + getCaseMessageInteractor, getCasesByUserInteractor, + getClosedCasesInteractor, getConsolidatedCasesByCaseInteractor, - getConsolidatedCasesByUserInteractor, getDocumentQCInboxForSectionInteractor, getDocumentQCInboxForUserInteractor, getDocumentQCServedForSectionInteractor, getDocumentQCServedForUserInteractor, getEligibleCasesForTrialSessionInteractor, + getInboxCaseMessagesForSectionInteractor, + getInboxCaseMessagesForUserInteractor, getInboxMessagesForSectionInteractor, getInboxMessagesForUserInteractor, getInternalUsersInteractor, @@ -309,6 +331,9 @@ const allUseCases = { getItemInteractor, getJudgeForUserChambersInteractor, getNotificationsInteractor, + getOpenConsolidatedCasesInteractor, + getOutboxCaseMessagesForSectionInteractor, + getOutboxCaseMessagesForUserInteractor, getPdfFromUrlInteractor, getPractitionerByBarNumberInteractor, getPractitionersByNameInteractor, @@ -357,8 +382,10 @@ const allUseCases = { updateCounselOnCaseInteractor, updateCourtIssuedDocketEntryInteractor, updateCourtIssuedOrderInteractor, + updateDeficiencyStatisticInteractor, updateDocketEntryInteractor, updateDocketEntryMetaInteractor, + updateOtherStatisticsInteractor, updatePetitionDetailsInteractor, updatePetitionerInformationInteractor, updatePractitionerUserInteractor, @@ -373,6 +400,7 @@ const allUseCases = { uploadDocumentInteractor, uploadExternalDocumentsInteractor, uploadOrderDocumentInteractor, + validateAddDeficiencyStatisticsInteractor, validateAddIrsPractitionerInteractor, validateAddPractitionerInteractor, validateAddPrivatePractitionerInteractor, @@ -381,6 +409,7 @@ const allUseCases = { validateCaseDeadlineInteractor, validateCaseDetailInteractor, validateCourtIssuedDocketEntryInteractor, + validateCreateCaseMessageInteractor, validateDocketEntryInteractor, validateDocketRecordInteractor, validateEditPrivatePractitionerInteractor, @@ -448,9 +477,9 @@ const applicationContext = { pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js'; return pdfjsLib; }, - getPdfStyles: async () => { - const pdfStyles = await import('../../shared/src/tools/pdfStyles.js'); - return pdfStyles; + getPdfLib: () => { + const pdfLib = import('pdf-lib'); + return pdfLib; }, getPersistenceGateway: () => { return { @@ -486,11 +515,13 @@ const applicationContext = { getUserPermissions, getUtilities: () => { return { + calculateISODate, compareCasesByDocketNumber, compareISODateStrings, compareStrings, createISODateString, createISODateStringFromObject, + dateStringsCompared, deconstructDate, filterEmptyStrings, formatCase, diff --git a/web-client/src/applicationContextPublic.js b/web-client/src/applicationContextPublic.js index 81cca086491..37ec3be2629 100644 --- a/web-client/src/applicationContextPublic.js +++ b/web-client/src/applicationContextPublic.js @@ -1,15 +1,23 @@ +import { + CASE_CAPTION_POSTFIX, + CASE_SEARCH_PAGE_SIZE, + COUNTRY_TYPES, + US_STATES, +} from '../../shared/src/business/entities/EntityConstants'; import { Case } from '../../shared/src/business/entities/cases/Case'; -import { CaseSearch } from '../../shared/src/business/entities/cases/CaseSearch'; -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; import { casePublicSearchInteractor } from '../../shared/src/proxies/casePublicSearchProxy'; import { compareCasesByDocketNumber } from '../../shared/src/business/utilities/getFormattedTrialSessionDetails'; -import { formatDateString } from '../../shared/src/business/utilities/DateHandler'; +import { + createISODateString, + formatDateString, +} from '../../shared/src/business/utilities/DateHandler'; import { formatDocketRecord, formatDocketRecordWithDocument, sortDocketRecords, } from '../../shared/src/business/utilities/getFormattedCaseDetail'; import { generatePublicDocketRecordPdfInteractor } from '../../shared/src/proxies/public/generatePublicDocketRecordPdfProxy'; +import { getCaseForPublicDocketSearchInteractor } from '../../shared/src/proxies/public/getCaseForPublicDocketNumberSearchProxy'; import { getCognitoLoginUrl, getPublicSiteUrl, @@ -17,6 +25,7 @@ import { import { getJudgeLastName } from '../../shared/src/business/utilities/getFormattedJudgeName'; import { getPublicCaseInteractor } from '../../shared/src/proxies/getPublicCaseProxy'; import { getPublicJudgesInteractor } from '../../shared/src/proxies/public/getPublicJudgesProxy'; +import { getTodaysOpinionsInteractor } from '../../shared/src/proxies/public/getTodaysOpinionsProxy'; import { opinionPublicSearchInteractor } from '../../shared/src/proxies/opinionPublicSearchProxy'; import { orderPublicSearchInteractor } from '../../shared/src/proxies/orderPublicSearchProxy'; import { validateCaseAdvancedSearchInteractor } from '../../shared/src/business/useCases/validateCaseAdvancedSearchInteractor'; @@ -25,6 +34,12 @@ import { validateOrderAdvancedSearchInteractor } from '../../shared/src/business import axios from 'axios'; import deepFreeze from 'deep-freeze'; +const ADVANCED_SEARCH_TABS = { + CASE: 'case', + OPINION: 'opinion', + ORDER: 'order', +}; + const applicationContextPublic = { getBaseUrl: () => { return process.env.API_URL || 'http://localhost:3000'; @@ -33,10 +48,11 @@ const applicationContextPublic = { getCognitoLoginUrl, getConstants: () => deepFreeze({ - CASE_CAPTION_POSTFIX: Case.CASE_CAPTION_POSTFIX, - CASE_SEARCH_PAGE_SIZE: CaseSearch.CASE_SEARCH_PAGE_SIZE, - COUNTRY_TYPES: ContactFactory.COUNTRY_TYPES, - US_STATES: ContactFactory.US_STATES, + ADVANCED_SEARCH_TABS, + CASE_CAPTION_POSTFIX: CASE_CAPTION_POSTFIX, + CASE_SEARCH_PAGE_SIZE: CASE_SEARCH_PAGE_SIZE, + COUNTRY_TYPES: COUNTRY_TYPES, + US_STATES: US_STATES, }), getCurrentUserToken: () => null, getHttpClient: () => axios, @@ -44,8 +60,10 @@ const applicationContextPublic = { getUseCases: () => ({ casePublicSearchInteractor, generatePublicDocketRecordPdfInteractor, + getCaseForPublicDocketSearchInteractor, getCaseInteractor: getPublicCaseInteractor, getPublicJudgesInteractor, + getTodaysOpinionsInteractor, opinionPublicSearchInteractor, orderPublicSearchInteractor, validateCaseAdvancedSearchInteractor, @@ -55,6 +73,7 @@ const applicationContextPublic = { getUtilities: () => { return { compareCasesByDocketNumber, + createISODateString, formatDateString, formatDocketRecord, formatDocketRecordWithDocument, diff --git a/web-client/src/getConstants.js b/web-client/src/getConstants.js index 05297f9f5d4..e57f1e327a3 100644 --- a/web-client/src/getConstants.js +++ b/web-client/src/getConstants.js @@ -1,83 +1,123 @@ import { + ADMISSIONS_STATUS_OPTIONS, + BUSINESS_TYPES, + CASE_CAPTION_POSTFIX, + CASE_SEARCH_PAGE_SIZE, + CASE_STATUS_TYPES, + CASE_TYPES, + CASE_TYPES_MAP, CHAMBERS_SECTION, CHAMBERS_SECTIONS, - SECTIONS, -} from '../../shared/src/business/entities/WorkQueue'; -import { Case } from '../../shared/src/business/entities/cases/Case'; -import { CaseInternal } from '../../shared/src/business/entities/cases/CaseInternal'; -import { CaseSearch } from '../../shared/src/business/entities/cases/CaseSearch'; -import { ContactFactory } from '../../shared/src/business/entities/contacts/ContactFactory'; -import { Document } from '../../shared/src/business/entities/Document'; -import { FORMATS } from '../../shared/src/business/utilities/DateHandler'; -import { + CHIEF_JUDGE, + CONTACT_CHANGE_DOCUMENT_TYPES, + COUNTRY_TYPES, + COURT_ISSUED_EVENT_CODES, + DEFAULT_PROCEDURE_TYPE, + DOCUMENT_CATEGORIES, + DOCUMENT_CATEGORY_MAP, + DOCUMENT_INTERNAL_CATEGORY_MAP, + DOCUMENT_NOTICE_EVENT_CODES, + EMPLOYER_OPTIONS, + ESTATE_TYPES, + FILING_TYPES, + INITIAL_DOCUMENT_TYPES, MAX_FILE_SIZE_BYTES, MAX_FILE_SIZE_MB, -} from '../../shared/src/persistence/s3/getUploadPolicy'; -import { Order } from '../../shared/src/business/entities/orders/Order'; -import { Practitioner } from '../../shared/src/business/entities/Practitioner'; + ORDER_TYPES, + OTHER_TYPES, + PARTY_TYPES, + PAYMENT_STATUS, + PRACTITIONER_TYPE_OPTIONS, + PROCEDURE_TYPES, + ROLES, + SCAN_MODES, + SECTIONS, + SERVICE_INDICATOR_TYPES, + SESSION_STATUS_GROUPS, + SESSION_TYPES, + SIGNED_DOCUMENT_TYPES, + STATUS_TYPES_MANUAL_UPDATE, + STATUS_TYPES_WITH_ASSOCIATED_JUDGE, + SYSTEM_GENERATED_DOCUMENT_TYPES, + TRANSCRIPT_EVENT_CODE, + TRIAL_CITIES, + TRIAL_STATUS_TYPES, + US_STATES, +} from '../../shared/src/business/entities/EntityConstants'; +import { FORMATS } from '../../shared/src/business/utilities/DateHandler'; import { ROLE_PERMISSIONS } from '../../shared/src/authorization/authorizationClientService'; -import { SERVICE_INDICATOR_TYPES } from '../../shared/src/business/entities/cases/CaseConstants'; import { SERVICE_STAMP_OPTIONS } from '../../shared/src/business/entities/courtIssuedDocument/CourtIssuedDocumentConstants'; -import { Scan } from '../../shared/src/business/entities/Scan'; -import { TrialSession } from '../../shared/src/business/entities/trialSessions/TrialSession'; -import { TrialSessionWorkingCopy } from '../../shared/src/business/entities/trialSessions/TrialSessionWorkingCopy'; -import { User } from '../../shared/src/business/entities/User'; const MINUTES = 60 * 1000; +const ADVANCED_SEARCH_TABS = { + CASE: 'case', + OPINION: 'opinion', + ORDER: 'order', + PRACTITIONER: 'practitioner', +}; + +const EXTERNAL_USER_DASHBOARD_TABS = { + CLOSED: 'Closed', + OPEN: 'Open', +}; + export const getConstants = () => ({ - ADMISSIONS_STATUS_OPTIONS: Practitioner.ADMISSIONS_STATUS_OPTIONS, - BUSINESS_TYPES: ContactFactory.BUSINESS_TYPES, - CASE_CAPTION_POSTFIX: Case.CASE_CAPTION_POSTFIX, + ADMISSIONS_STATUS_OPTIONS, + ADVANCED_SEARCH_TABS, + BUSINESS_TYPES: BUSINESS_TYPES, + CASE_CAPTION_POSTFIX, CASE_INVENTORY_PAGE_SIZE: 2, - CASE_SEARCH_PAGE_SIZE: CaseSearch.CASE_SEARCH_PAGE_SIZE, - CASE_TYPES: Case.CASE_TYPES, - CASE_TYPES_MAP: Case.CASE_TYPES_MAP, - CATEGORIES: Document.CATEGORIES, - CATEGORY_MAP: Document.CATEGORY_MAP, + CASE_LIST_PAGE_SIZE: 20, + CASE_SEARCH_PAGE_SIZE, + CASE_TYPES, + CASE_TYPES_MAP, + CATEGORIES: DOCUMENT_CATEGORIES, + CATEGORY_MAP: DOCUMENT_CATEGORY_MAP, CHAMBERS_SECTION, CHAMBERS_SECTIONS, - CHIEF_JUDGE: Case.CHIEF_JUDGE, - CONTACT_CHANGE_DOCUMENT_TYPES: Document.CONTACT_CHANGE_DOCUMENT_TYPES, - COUNTRY_TYPES: ContactFactory.COUNTRY_TYPES, - COURT_ISSUED_EVENT_CODES: Document.COURT_ISSUED_EVENT_CODES, + CHIEF_JUDGE, + CONTACT_CHANGE_DOCUMENT_TYPES: CONTACT_CHANGE_DOCUMENT_TYPES, + COUNTRY_TYPES, + COURT_ISSUED_EVENT_CODES: COURT_ISSUED_EVENT_CODES, DATE_FORMATS: FORMATS, - DEFAULT_PROCEDURE_TYPE: CaseInternal.DEFAULT_PROCEDURE_TYPE, - EMPLOYER_OPTIONS: Practitioner.EMPLOYER_OPTIONS, - ESTATE_TYPES: ContactFactory.ESTATE_TYPES, - FILING_TYPES: Case.FILING_TYPES, - INITIAL_DOCUMENT_TYPES: Document.INITIAL_DOCUMENT_TYPES, - INTERNAL_CATEGORY_MAP: Document.INTERNAL_CATEGORY_MAP, + DEFAULT_PROCEDURE_TYPE, + EMPLOYER_OPTIONS, + ESTATE_TYPES, + EXTERNAL_USER_DASHBOARD_TABS, + FILING_TYPES, + INITIAL_DOCUMENT_TYPES, + INTERNAL_CATEGORY_MAP: DOCUMENT_INTERNAL_CATEGORY_MAP, MAX_FILE_SIZE_BYTES, MAX_FILE_SIZE_MB, - NOTICE_EVENT_CODES: Document.NOTICE_EVENT_CODES, - ORDER_TYPES_MAP: Order.ORDER_TYPES, - OTHER_TYPES: ContactFactory.OTHER_TYPES, - PARTY_TYPES: ContactFactory.PARTY_TYPES, - PAYMENT_STATUS: Case.PAYMENT_STATUS, - PRACTITIONER_TYPE_OPTIONS: Practitioner.PRACTITIONER_TYPE_OPTIONS, - PROCEDURE_TYPES: Case.PROCEDURE_TYPES, + NOTICE_EVENT_CODES: DOCUMENT_NOTICE_EVENT_CODES, + ORDER_TYPES_MAP: ORDER_TYPES, + OTHER_TYPES, + PARTY_TYPES, + PAYMENT_STATUS, + PRACTITIONER_TYPE_OPTIONS, + PROCEDURE_TYPES, REFRESH_INTERVAL: 20 * MINUTES, ROLE_PERMISSIONS, - SCAN_MODES: Scan.SCAN_MODES, + SCAN_MODES, SECTIONS, SERVICE_INDICATOR_TYPES, SERVICE_STAMP_OPTIONS, SESSION_DEBOUNCE: 250, SESSION_MODAL_TIMEOUT: 5 * MINUTES, - SESSION_STATUS_GROUPS: TrialSession.SESSION_STATUS_GROUPS, + SESSION_STATUS_GROUPS, SESSION_TIMEOUT: (process.env.SESSION_TIMEOUT && parseInt(process.env.SESSION_TIMEOUT)) || 55 * MINUTES, - SIGNED_DOCUMENT_TYPES: Document.SIGNED_DOCUMENT_TYPES, - STATUS_TYPES: Case.STATUS_TYPES, - STATUS_TYPES_MANUAL_UPDATE: Case.STATUS_TYPES_MANUAL_UPDATE, - STATUS_TYPES_WITH_ASSOCIATED_JUDGE: Case.STATUS_TYPES_WITH_ASSOCIATED_JUDGE, - SYSTEM_GENERATED_DOCUMENT_TYPES: Document.SYSTEM_GENERATED_DOCUMENT_TYPES, - TRANSCRIPT_EVENT_CODE: Document.TRANSCRIPT_EVENT_CODE, - TRIAL_CITIES: TrialSession.TRIAL_CITIES, - TRIAL_SESSION_TYPES: TrialSession.SESSION_TYPES, - TRIAL_STATUS_TYPES: TrialSessionWorkingCopy.TRIAL_STATUS_TYPES, - US_STATES: ContactFactory.US_STATES, - USER_ROLES: User.ROLES, + SIGNED_DOCUMENT_TYPES: SIGNED_DOCUMENT_TYPES, + STATUS_TYPES: CASE_STATUS_TYPES, + STATUS_TYPES_MANUAL_UPDATE, + STATUS_TYPES_WITH_ASSOCIATED_JUDGE: STATUS_TYPES_WITH_ASSOCIATED_JUDGE, + SYSTEM_GENERATED_DOCUMENT_TYPES: SYSTEM_GENERATED_DOCUMENT_TYPES, + TRANSCRIPT_EVENT_CODE: TRANSCRIPT_EVENT_CODE, + TRIAL_CITIES: TRIAL_CITIES, + TRIAL_SESSION_TYPES: SESSION_TYPES, + TRIAL_STATUS_TYPES, + US_STATES, + USER_ROLES: ROLES, }); diff --git a/web-client/src/index.pug b/web-client/src/index.pug index d32f3fb05e6..daad13f706a 100644 --- a/web-client/src/index.pug +++ b/web-client/src/index.pug @@ -2,7 +2,6 @@ doctype html html(lang="en") head meta(charset="utf-8") - meta(built="" + (new Date()).toLocaleString('en-US', {weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZone: 'America/New_York'}) + " EST") meta(revision="" + (process.env.CIRCLE_SHA1 || '').substr(0,7)) meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no") title U.S. Tax Court Electronic Filing and Case Management System @@ -18,4 +17,6 @@ html(lang="en") #app #modal-root #dwtcontrolContainer + footer.grid-container.position-fixed.bottom-0.right-0 + p Deployed #{(new Date()).toLocaleString('en-US', {weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZone: 'America/New_York'})} EST script(src="index." + process.env.USTC_ENV + ".js") diff --git a/web-client/src/presenter/actions/AdvancedSearch/defaultAdvancedSearchFormAction.test.js b/web-client/src/presenter/actions/AdvancedSearch/defaultAdvancedSearchFormAction.test.js index 53b1c29a7fa..8f055f5cd3e 100644 --- a/web-client/src/presenter/actions/AdvancedSearch/defaultAdvancedSearchFormAction.test.js +++ b/web-client/src/presenter/actions/AdvancedSearch/defaultAdvancedSearchFormAction.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../../../../shared/src/business/entities/contacts/ContactFactory'; +import { COUNTRY_TYPES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { defaultAdvancedSearchFormAction } from './defaultAdvancedSearchFormAction'; import { presenter } from '../../presenter-mock'; @@ -18,7 +18,7 @@ describe('defaultAdvancedSearchFormAction', () => { expect(result.state.advancedSearchForm).toEqual({ caseSearchByDocketNumber: {}, caseSearchByName: { - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, }, opinionSearch: {}, orderSearch: {}, @@ -46,7 +46,7 @@ describe('defaultAdvancedSearchFormAction', () => { advancedSearchForm: { caseSearchByDocketNumber: { yes: true }, caseSearchByName: { - countryType: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, + countryType: COUNTRY_TYPES.INTERNATIONAL, no: false, }, opinionSearch: {}, @@ -61,7 +61,7 @@ describe('defaultAdvancedSearchFormAction', () => { expect(result.state.advancedSearchForm).toEqual({ caseSearchByDocketNumber: { yes: true }, caseSearchByName: { - countryType: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, + countryType: COUNTRY_TYPES.INTERNATIONAL, no: false, }, opinionSearch: {}, diff --git a/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.js b/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.js index 76ea8bcb268..1ef5051022f 100644 --- a/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.js +++ b/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.js @@ -1,4 +1,6 @@ +import { clone } from 'lodash'; import { state } from 'cerebral'; +import { trimDocketNumberSearch } from '../setCaseIdFromSearchAction'; /** * submit advanced search form to search for opinions @@ -12,7 +14,13 @@ export const submitOpinionAdvancedSearchAction = async ({ applicationContext, get, }) => { - const searchParams = get(state.advancedSearchForm.opinionSearch); + const searchParams = clone(get(state.advancedSearchForm.opinionSearch)); + + if (searchParams.docketNumber) { + searchParams.docketNumber = trimDocketNumberSearch( + searchParams.docketNumber, + ); + } const searchResults = await applicationContext .getUseCases() diff --git a/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.test.js b/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.test.js index f3a70136210..7758d33cca2 100644 --- a/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.test.js +++ b/web-client/src/presenter/actions/AdvancedSearch/submitOpinionAdvancedSearchAction.test.js @@ -33,4 +33,34 @@ describe('submitOpinionAdvancedSearchAction', () => { }, }); }); + + it('should remove the docketNumberSuffix when a docket number is present', async () => { + await runAction(submitOpinionAdvancedSearchAction, { + modules: { + presenter, + }, + state: { + advancedSearchForm: { + opinionSearch: { + docketNumber: '105-20L', + keyword: 'a', + }, + }, + }, + }); + + expect( + applicationContext.getUseCases().opinionAdvancedSearchInteractor.mock + .calls.length, + ).toEqual(1); + expect( + applicationContext.getUseCases().opinionAdvancedSearchInteractor.mock + .calls[0][0], + ).toMatchObject({ + searchParams: { + docketNumber: '105-20', + keyword: 'a', + }, + }); + }); }); diff --git a/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.js b/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.js index 8cb71229f62..b322b79478f 100644 --- a/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.js +++ b/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.js @@ -1,4 +1,6 @@ +import { clone } from 'lodash'; import { state } from 'cerebral'; +import { trimDocketNumberSearch } from '../setCaseIdFromSearchAction'; /** * submit advanced search form @@ -12,8 +14,13 @@ export const submitOrderAdvancedSearchAction = async ({ applicationContext, get, }) => { - const searchParams = get(state.advancedSearchForm.orderSearch); + const searchParams = clone(get(state.advancedSearchForm.orderSearch)); + if (searchParams.docketNumber) { + searchParams.docketNumber = trimDocketNumberSearch( + searchParams.docketNumber, + ); + } const searchResults = await applicationContext .getUseCases() .orderAdvancedSearchInteractor({ diff --git a/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.test.js b/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.test.js index 2bfefd153b8..8b4b49b8e18 100644 --- a/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.test.js +++ b/web-client/src/presenter/actions/AdvancedSearch/submitOrderAdvancedSearchAction.test.js @@ -33,4 +33,34 @@ describe('submitOrderAdvancedSearchAction', () => { }, }); }); + + it('should remove the docketNumberSuffix when a docket number is present', async () => { + await runAction(submitOrderAdvancedSearchAction, { + modules: { + presenter, + }, + state: { + advancedSearchForm: { + orderSearch: { + docketNumber: '105-20L', + keyword: 'a', + }, + }, + }, + }); + + expect( + applicationContext.getUseCases().orderAdvancedSearchInteractor.mock.calls + .length, + ).toEqual(1); + expect( + applicationContext.getUseCases().orderAdvancedSearchInteractor.mock + .calls[0][0], + ).toMatchObject({ + searchParams: { + docketNumber: '105-20', + keyword: 'a', + }, + }); + }); }); diff --git a/web-client/src/presenter/actions/CaseDetail/checkForOrdersNeededAction.js b/web-client/src/presenter/actions/CaseDetail/checkForOrdersNeededAction.js deleted file mode 100644 index 89406dd8289..00000000000 --- a/web-client/src/presenter/actions/CaseDetail/checkForOrdersNeededAction.js +++ /dev/null @@ -1,46 +0,0 @@ -import { state } from 'cerebral'; - -/** - * returns a path based upon if any order-related values for a given case - * are set to true, in which it returns path.yes(); otherwise, path.no() - * - * @param {object} providers the providers object - * @param {object} providers.get the cerebral get function - * @param {object} providers.path the paths for next execution - * @returns {object} the path to execute next - */ -export const checkForOrdersNeededAction = async ({ get, path }) => { - const caseDetail = get(state.caseDetail); - - if (!caseDetail) { - return path.no(); - } - - const { - noticeOfAttachments, - orderDesignatingPlaceOfTrial, - orderForAmendedPetition, - orderForAmendedPetitionAndFilingFee, - orderForFilingFee, - orderForOds, - orderForRatification, - orderToShowCause, - } = caseDetail; - - const ordersNeeded = [ - orderForAmendedPetition, - orderForAmendedPetitionAndFilingFee, - orderForFilingFee, - orderForOds, - orderForRatification, - orderToShowCause, - noticeOfAttachments, - orderDesignatingPlaceOfTrial, - ].some(val => !!val); - - if (ordersNeeded) { - return path.yes(); - } - - return path.no(); -}; diff --git a/web-client/src/presenter/actions/CaseDetail/checkForOrdersNeededAction.test.js b/web-client/src/presenter/actions/CaseDetail/checkForOrdersNeededAction.test.js deleted file mode 100644 index aa87c12579d..00000000000 --- a/web-client/src/presenter/actions/CaseDetail/checkForOrdersNeededAction.test.js +++ /dev/null @@ -1,60 +0,0 @@ -import { checkForOrdersNeededAction } from './checkForOrdersNeededAction'; -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('checkForOrdersNeededAction', () => { - let yesStub; - let noStub; - - beforeAll(() => { - yesStub = jest.fn(); - noStub = jest.fn(); - - presenter.providers.path = { - no: noStub, - yes: yesStub, - }; - }); - - it('should select yes when any one order (or notice) is needed for a specific case', async () => { - await runAction(checkForOrdersNeededAction, { - modules: { - presenter, - }, - state: { - caseDetail: { - noticeOfAttachments: false, - orderForAmendedPetition: true, - orderForAmendedPetitionAndFilingFee: false, - orderForFilingFee: false, - orderForOds: true, - orderForRatification: false, - orderToShowCause: true, - }, - }, - }); - - expect(yesStub).toHaveBeenCalled(); - }); - - it('should call no path there are not any orders needed for a case', async () => { - await runAction(checkForOrdersNeededAction, { - modules: { - presenter, - }, - state: { - caseDetail: { - noticeOfAttachments: false, - orderForAmendedPetition: false, - orderForAmendedPetitionAndFilingFee: false, - orderForFilingFee: false, - orderForOds: false, - orderForRatification: false, - orderToShowCause: false, - }, - }, - }); - - expect(noStub).toHaveBeenCalled(); - }); -}); diff --git a/web-client/src/presenter/actions/CaseDetail/createCaseMessageAction.js b/web-client/src/presenter/actions/CaseDetail/createCaseMessageAction.js new file mode 100644 index 00000000000..6334d6a75f3 --- /dev/null +++ b/web-client/src/presenter/actions/CaseDetail/createCaseMessageAction.js @@ -0,0 +1,19 @@ +import { state } from 'cerebral'; + +export const createCaseMessageAction = async ({ applicationContext, get }) => { + const form = get(state.modal.form); + + const { caseId } = get(state.caseDetail); + + await applicationContext.getUseCases().createCaseMessageInteractor({ + applicationContext, + caseId, + ...form, + }); + + return { + alertSuccess: { + message: 'Your message has been sent.', + }, + }; +}; diff --git a/web-client/src/presenter/actions/CaseDetail/createCaseMessageAction.test.js b/web-client/src/presenter/actions/CaseDetail/createCaseMessageAction.test.js new file mode 100644 index 00000000000..7391d264753 --- /dev/null +++ b/web-client/src/presenter/actions/CaseDetail/createCaseMessageAction.test.js @@ -0,0 +1,54 @@ +import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; +import { createCaseMessageAction } from './createCaseMessageAction'; +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('createCaseMessageAction', () => { + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + }); + + it('should call createCaseMessageInteractor with the expected parameters and return the alertSuccess', async () => { + const result = await runAction(createCaseMessageAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: 'a7806fa0-ce6a-41ca-b66e-59438953f8bb', + }, + modal: { + form: { + attachments: [ + { + documentId: 'b1130321-0a76-43bc-b3eb-64a18f079873', + documentTitle: 'Petition', + }, + ], + message: 'You there!', + subject: 'Hey!', + }, + }, + }, + }); + + expect( + applicationContext.getUseCases().createCaseMessageInteractor, + ).toBeCalled(); + expect( + applicationContext.getUseCases().createCaseMessageInteractor.mock + .calls[0][0], + ).toMatchObject({ + attachments: [ + { + documentId: 'b1130321-0a76-43bc-b3eb-64a18f079873', + documentTitle: 'Petition', + }, + ], + caseId: 'a7806fa0-ce6a-41ca-b66e-59438953f8bb', + message: 'You there!', + subject: 'Hey!', + }); + expect(result.output).toHaveProperty('alertSuccess'); + }); +}); diff --git a/web-client/src/presenter/actions/CaseDetail/shouldGetTrialSessionWorkingCopyAction.js b/web-client/src/presenter/actions/CaseDetail/shouldGetTrialSessionWorkingCopyAction.js deleted file mode 100644 index 22efea64678..00000000000 --- a/web-client/src/presenter/actions/CaseDetail/shouldGetTrialSessionWorkingCopyAction.js +++ /dev/null @@ -1,19 +0,0 @@ -import { state } from 'cerebral'; - -/** - * returns the correct path depending on whether the trial session id is set in the state - * - * @param {object} providers the providers object - * @param {object} providers.get the cerebral get function - * @param {object} providers.path the paths for next execution - * @returns {object} the path to execute next - */ -export const shouldGetTrialSessionWorkingCopyAction = async ({ get, path }) => { - const trialSessionId = get(state.caseDetail.trialSessionId); - - if (trialSessionId) { - return path.yes({ trialSessionId }); - } else { - return path.no(); - } -}; diff --git a/web-client/src/presenter/actions/CaseDetail/shouldGetTrialSessionWorkingCopyAction.test.js b/web-client/src/presenter/actions/CaseDetail/shouldGetTrialSessionWorkingCopyAction.test.js deleted file mode 100644 index 5be0bfee0d9..00000000000 --- a/web-client/src/presenter/actions/CaseDetail/shouldGetTrialSessionWorkingCopyAction.test.js +++ /dev/null @@ -1,43 +0,0 @@ -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { shouldGetTrialSessionWorkingCopyAction } from './shouldGetTrialSessionWorkingCopyAction'; - -describe('shouldGetTrialSessionWorkingCopyAction', () => { - let yesStub; - let noStub; - - beforeAll(() => { - yesStub = jest.fn(); - noStub = jest.fn(); - - presenter.providers.path = { - no: noStub, - yes: yesStub, - }; - }); - - it('should select yes when trial session exists', async () => { - await runAction(shouldGetTrialSessionWorkingCopyAction, { - modules: { - presenter, - }, - state: { - caseDetail: { - trialSessionId: '123', - }, - }, - }); - - expect(yesStub).toHaveBeenCalled(); - }); - - it('should call no path when no trial session exists', async () => { - await runAction(shouldGetTrialSessionWorkingCopyAction, { - modules: { - presenter, - }, - }); - - expect(noStub).toHaveBeenCalled(); - }); -}); diff --git a/web-client/src/presenter/actions/CaseDetail/validateUpdateCaseModalAction.test.js b/web-client/src/presenter/actions/CaseDetail/validateUpdateCaseModalAction.test.js index 8c4a854e3c5..36f2a09a5fa 100644 --- a/web-client/src/presenter/actions/CaseDetail/validateUpdateCaseModalAction.test.js +++ b/web-client/src/presenter/actions/CaseDetail/validateUpdateCaseModalAction.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -28,7 +28,7 @@ describe('validateUpdateCaseModalAction', () => { state: { modal: { caseCaption: 'A case caption', - caseStatus: Case.STATUS_TYPES.closed, + caseStatus: CASE_STATUS_TYPES.closed, }, }, }); @@ -46,7 +46,7 @@ describe('validateUpdateCaseModalAction', () => { modal: { associatedJudge: 'Judge Armen', caseCaption: 'A case caption', - caseStatus: Case.STATUS_TYPES.submitted, + caseStatus: CASE_STATUS_TYPES.submitted, }, }, }); @@ -63,7 +63,7 @@ describe('validateUpdateCaseModalAction', () => { state: { modal: { associatedJudge: 'Judge Armen', - caseStatus: Case.STATUS_TYPES.submitted, + caseStatus: CASE_STATUS_TYPES.submitted, }, }, }); @@ -97,7 +97,7 @@ describe('validateUpdateCaseModalAction', () => { state: { modal: { caseCaption: 'A case caption', - caseStatus: Case.STATUS_TYPES.submitted, + caseStatus: CASE_STATUS_TYPES.submitted, }, }, }); diff --git a/web-client/src/presenter/actions/CaseInventoryReport/generatePrintableCaseInventoryReportAction.js b/web-client/src/presenter/actions/CaseInventoryReport/generatePrintableCaseInventoryReportAction.js index 9d5626f0fef..ebeccdc8798 100644 --- a/web-client/src/presenter/actions/CaseInventoryReport/generatePrintableCaseInventoryReportAction.js +++ b/web-client/src/presenter/actions/CaseInventoryReport/generatePrintableCaseInventoryReportAction.js @@ -6,6 +6,7 @@ import { state } from 'cerebral'; * @param {object} providers the providers object * @param {object} providers.applicationContext the application context * @param {Function} providers.get the cerebral get function + * @returns {Promise} the url of the printable pdf */ export const generatePrintableCaseInventoryReportAction = async ({ applicationContext, diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/deleteCorrespondenceDocumentAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/deleteCorrespondenceDocumentAction.js new file mode 100644 index 00000000000..0a9a1196856 --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/deleteCorrespondenceDocumentAction.js @@ -0,0 +1,33 @@ +import { state } from 'cerebral'; + +/** + * delete correspondence document from a case. + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {Function} providers.get the cerebral get helper function + * @param {object} providers.path the cerebral path which contains the next path in the sequence (path of success or error) + * @returns {object} the next path based on if deleting the correspondence document was successful or not + */ +export const deleteCorrespondenceDocumentAction = async ({ + applicationContext, + get, + path, +}) => { + const caseId = get(state.caseDetail.caseId); + const documentId = get(state.modal.correspondenceToDelete.documentId); + + try { + await applicationContext + .getUseCases() + .deleteCorrespondenceDocumentInteractor({ + applicationContext, + caseId, + documentId, + }); + + return path.success(); + } catch (err) { + return path.error(); + } +}; diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/deleteCorrespondenceDocumentAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/deleteCorrespondenceDocumentAction.test.js new file mode 100644 index 00000000000..469400e5a3d --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/deleteCorrespondenceDocumentAction.test.js @@ -0,0 +1,62 @@ +import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; +import { deleteCorrespondenceDocumentAction } from './deleteCorrespondenceDocumentAction'; +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('deleteCorrespondenceDocumentAction', () => { + const successStub = jest.fn(); + const errorStub = jest.fn(); + + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + + presenter.providers.path = { + error: errorStub, + success: successStub, + }; + }); + + it('should return the success path when the correspondence document was successfully removed from the case', async () => { + await runAction(deleteCorrespondenceDocumentAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + }, + modal: { + correspondenceToDelete: { + documentId: 'abc', + }, + }, + }, + }); + + expect(successStub).toHaveBeenCalled(); + }); + + it('should return the error path when an error occurred removing the correspondence document', async () => { + applicationContext + .getUseCases() + .deleteCorrespondenceDocumentInteractor.mockRejectedValue(new Error()); + + await runAction(deleteCorrespondenceDocumentAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + }, + modal: { + correspondenceToDelete: { + documentId: 'abc', + }, + }, + }, + }); + + expect(errorStub).toHaveBeenCalled(); + }); +}); diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertErrorAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertErrorAction.js new file mode 100644 index 00000000000..4d3f2ada3f7 --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertErrorAction.js @@ -0,0 +1,13 @@ +/** + * get alert message when a correspondence fails to delete + * + * @returns {object} the prop of the alert error message + */ +export const getDeleteCorrespondenceDocumentAlertErrorAction = () => { + return { + alertError: { + title: + 'An error occurred when attempting to delete the correspondence document.', + }, + }; +}; diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertErrorAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertErrorAction.test.js new file mode 100644 index 00000000000..b09d7ebd8cf --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertErrorAction.test.js @@ -0,0 +1,12 @@ +import { getDeleteCorrespondenceDocumentAlertErrorAction } from './getDeleteCorrespondenceDocumentAlertErrorAction'; +import { runAction } from 'cerebral/test'; + +describe('getDeleteCorrespondenceDocumentAlertErrorAction', () => { + it('should return alertError prop', async () => { + const result = await runAction( + getDeleteCorrespondenceDocumentAlertErrorAction, + {}, + ); + expect(result.output.alertError).toBeTruthy(); + }); +}); diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertSuccessAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertSuccessAction.js new file mode 100644 index 00000000000..a643064823c --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertSuccessAction.js @@ -0,0 +1,12 @@ +/** + * get alert message when a correspondence is successfully deleted + * + * @returns {object} the prop of the alert success message + */ +export const getDeleteCorrespondenceDocumentAlertSuccessAction = () => { + return { + alertSuccess: { + message: 'Document removed from Correspondence Files.', + }, + }; +}; diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertSuccessAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertSuccessAction.test.js new file mode 100644 index 00000000000..d940e16411d --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertSuccessAction.test.js @@ -0,0 +1,12 @@ +import { getDeleteCorrespondenceDocumentAlertSuccessAction } from './getDeleteCorrespondenceDocumentAlertSuccessAction'; +import { runAction } from 'cerebral/test'; + +describe('getDeleteCorrespondenceDocumentAlertSuccessAction', () => { + it('should return alertSuccess prop', async () => { + const result = await runAction( + getDeleteCorrespondenceDocumentAlertSuccessAction, + {}, + ); + expect(result.output.alertSuccess).toBeTruthy(); + }); +}); diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/getEditCorrespondenceDocumentAlertSuccessAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/getEditCorrespondenceDocumentAlertSuccessAction.js new file mode 100644 index 00000000000..41c41c892e4 --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/getEditCorrespondenceDocumentAlertSuccessAction.js @@ -0,0 +1,12 @@ +/** + * get alert message when a correspondence is successfully updated + * + * @returns {object} the prop of the alert success message + */ +export const getEditCorrespondenceDocumentAlertSuccessAction = () => { + return { + alertSuccess: { + message: 'Correspondence has been successfully updated.', + }, + }; +}; diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/getEditCorrespondenceDocumentAlertSuccessAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/getEditCorrespondenceDocumentAlertSuccessAction.test.js new file mode 100644 index 00000000000..4128c44c4bb --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/getEditCorrespondenceDocumentAlertSuccessAction.test.js @@ -0,0 +1,12 @@ +import { getEditCorrespondenceDocumentAlertSuccessAction } from './getEditCorrespondenceDocumentAlertSuccessAction'; +import { runAction } from 'cerebral/test'; + +describe('getEditCorrespondenceDocumentAlertSuccessAction', () => { + it('should return alertSuccess prop', async () => { + const result = await runAction( + getEditCorrespondenceDocumentAlertSuccessAction, + {}, + ); + expect(result.output.alertSuccess).toBeTruthy(); + }); +}); diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.js similarity index 100% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.js rename to web-client/src/presenter/actions/CorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.js diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.test.js similarity index 100% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.test.js rename to web-client/src/presenter/actions/CorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction.test.js diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/setCorrespondenceToDeleteAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/setCorrespondenceToDeleteAction.js new file mode 100644 index 00000000000..9cb527c2edc --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/setCorrespondenceToDeleteAction.js @@ -0,0 +1,15 @@ +import { state } from 'cerebral'; + +/** + * set correspondence to delete information. + * + * @param {object} providers the providers object + * @param {object} providers.props the cerebral props object + * @param {object} providers.store the cerebral store + */ +export const setCorrespondenceToDeleteAction = async ({ props, store }) => { + const { documentId, documentTitle } = props; + + store.set(state.modal.correspondenceToDelete.documentTitle, documentTitle); + store.set(state.modal.correspondenceToDelete.documentId, documentId); +}; diff --git a/web-client/src/presenter/actions/CorrespondenceDocument/setCorrespondenceToDeleteAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/setCorrespondenceToDeleteAction.test.js new file mode 100644 index 00000000000..74bfffd41a1 --- /dev/null +++ b/web-client/src/presenter/actions/CorrespondenceDocument/setCorrespondenceToDeleteAction.test.js @@ -0,0 +1,23 @@ +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { setCorrespondenceToDeleteAction } from './setCorrespondenceToDeleteAction'; + +describe('setCorrespondenceToDeleteAction', () => { + it('should set state.modal.correspondenceToDelete documentTitle and documentId', async () => { + const result = await runAction(setCorrespondenceToDeleteAction, { + modules: { + presenter, + }, + props: { + documentId: '123', + documentTitle: 'correspondence to delete', + }, + state: {}, + }); + + expect(result.state.modal.correspondenceToDelete).toMatchObject({ + documentId: '123', + documentTitle: 'correspondence to delete', + }); + }); +}); diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/setDocumentTitleFromFreeTextAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/setDocumentTitleFromFormAction.js similarity index 63% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/setDocumentTitleFromFreeTextAction.js rename to web-client/src/presenter/actions/CorrespondenceDocument/setDocumentTitleFromFormAction.js index 7c9f9d4d964..b0f984125f2 100644 --- a/web-client/src/presenter/actions/UploadCorrespondenceDocument/setDocumentTitleFromFreeTextAction.js +++ b/web-client/src/presenter/actions/CorrespondenceDocument/setDocumentTitleFromFormAction.js @@ -7,6 +7,6 @@ import { state } from 'cerebral'; * @param {object} providers.store the cerebral store * @param {object} providers.get the get function */ -export const setDocumentTitleFromFreeTextAction = ({ get, store }) => { - store.set(state.form.documentTitle, get(state.form.freeText)); +export const setDocumentTitleFromFormAction = ({ get, store }) => { + store.set(state.form.documentTitle, get(state.form.documentTitle)); }; diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/setDocumentTitleFromFreeTextAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/setDocumentTitleFromFormAction.test.js similarity index 57% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/setDocumentTitleFromFreeTextAction.test.js rename to web-client/src/presenter/actions/CorrespondenceDocument/setDocumentTitleFromFormAction.test.js index b5e77cf41a7..8d994cb3cd9 100644 --- a/web-client/src/presenter/actions/UploadCorrespondenceDocument/setDocumentTitleFromFreeTextAction.test.js +++ b/web-client/src/presenter/actions/CorrespondenceDocument/setDocumentTitleFromFormAction.test.js @@ -1,16 +1,16 @@ import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; -import { setDocumentTitleFromFreeTextAction } from './setDocumentTitleFromFreeTextAction'; +import { setDocumentTitleFromFormAction } from './setDocumentTitleFromFormAction'; -describe('setDocumentTitleFromFreeTextAction', () => { +describe('setDocumentTitleFromFormAction', () => { it('should update the forms document title', async () => { - const results = await runAction(setDocumentTitleFromFreeTextAction, { + const results = await runAction(setDocumentTitleFromFormAction, { modules: { presenter, }, state: { form: { - freeText: 'something', + documentTitle: 'something', }, }, }); diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/submitCorrespondenceAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/submitCorrespondenceAction.js similarity index 97% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/submitCorrespondenceAction.js rename to web-client/src/presenter/actions/CorrespondenceDocument/submitCorrespondenceAction.js index 4e014ae2560..b3ede3d0bfa 100644 --- a/web-client/src/presenter/actions/UploadCorrespondenceDocument/submitCorrespondenceAction.js +++ b/web-client/src/presenter/actions/CorrespondenceDocument/submitCorrespondenceAction.js @@ -19,6 +19,7 @@ export const submitCorrespondenceAction = async ({ const { caseId, docketNumber } = get(state.caseDetail); const { primaryDocumentFileId: documentId } = props; const formData = get(state.form); + const { documentIdToEdit } = formData; let documentMetadata = omit(formData, [ @@ -47,7 +48,7 @@ export const submitCorrespondenceAction = async ({ .getUseCases() .updateCorrespondenceDocumentInteractor({ applicationContext, - documentIdToEdit, + documentId: documentIdToEdit, documentMetadata, }); } else { diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/submitCorrespondenceAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/submitCorrespondenceAction.test.js similarity index 96% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/submitCorrespondenceAction.test.js rename to web-client/src/presenter/actions/CorrespondenceDocument/submitCorrespondenceAction.test.js index 2ccd81f4682..5febfdae991 100644 --- a/web-client/src/presenter/actions/UploadCorrespondenceDocument/submitCorrespondenceAction.test.js +++ b/web-client/src/presenter/actions/CorrespondenceDocument/submitCorrespondenceAction.test.js @@ -58,7 +58,7 @@ describe('submitCorrespondenceAction', () => { }); }); - it('updates an existing document for correspondence', async () => { + it('updates an existing document for correspondence when state.form has a documentIdToEdit', async () => { const result = await runAction(submitCorrespondenceAction, { modules: { presenter, diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/uploadCorrespondenceFileAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/uploadCorrespondenceFileAction.js similarity index 100% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/uploadCorrespondenceFileAction.js rename to web-client/src/presenter/actions/CorrespondenceDocument/uploadCorrespondenceFileAction.js diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/uploadCorrespondenceFileAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/uploadCorrespondenceFileAction.test.js similarity index 100% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/uploadCorrespondenceFileAction.test.js rename to web-client/src/presenter/actions/CorrespondenceDocument/uploadCorrespondenceFileAction.test.js diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction.js b/web-client/src/presenter/actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction.js similarity index 79% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction.js rename to web-client/src/presenter/actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction.js index d855cf21ddc..24b8c95a56b 100644 --- a/web-client/src/presenter/actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction.js +++ b/web-client/src/presenter/actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction.js @@ -10,13 +10,13 @@ import { state } from 'cerebral'; * @returns {object} the next path based on if validation was successful or error */ export const validateUploadCorrespondenceDocumentAction = ({ get, path }) => { - const { freeText, primaryDocumentFile } = get(state.form); + const { documentTitle, primaryDocumentFile } = get(state.form); let errors = {}; - let errorDisplayOrder = ['freeText', 'primaryDocumentFile']; + let errorDisplayOrder = ['documentTitle', 'primaryDocumentFile']; - if (!freeText) { - errors.freeText = 'Enter a description'; + if (!documentTitle) { + errors.documentTitle = 'Enter a description'; } if (!primaryDocumentFile) { diff --git a/web-client/src/presenter/actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction.test.js b/web-client/src/presenter/actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction.test.js similarity index 84% rename from web-client/src/presenter/actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction.test.js rename to web-client/src/presenter/actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction.test.js index 278cf65c284..28b584355b7 100644 --- a/web-client/src/presenter/actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction.test.js +++ b/web-client/src/presenter/actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction.test.js @@ -4,29 +4,26 @@ import { runAction } from 'cerebral/test'; import { validateUploadCorrespondenceDocumentAction } from './validateUploadCorrespondenceDocumentAction'; describe('validateUploadCorrespondenceDocumentAction', () => { - let successStub; - let errorStub; + const successStub = jest.fn(); + const errorStub = jest.fn(); beforeAll(() => { presenter.providers.applicationContext = applicationContext; - successStub = jest.fn(); - errorStub = jest.fn(); - presenter.providers.path = { error: errorStub, success: successStub, }; }); - it('should call path.success and not path.error if freeText and primaryDocumentFile are defined', async () => { + it('should call path.success and not path.error if documentTitle and primaryDocumentFile are defined', async () => { await runAction(validateUploadCorrespondenceDocumentAction, { modules: { presenter, }, state: { form: { - freeText: 'Some text', + documentTitle: 'Some text', primaryDocumentFile: '01010101', }, }, diff --git a/web-client/src/presenter/actions/CourtIssuedDocketEntry/setDefaultServiceStampAction.test.js b/web-client/src/presenter/actions/CourtIssuedDocketEntry/setDefaultServiceStampAction.test.js index f588bbac325..02dcf5ff4cf 100644 --- a/web-client/src/presenter/actions/CourtIssuedDocketEntry/setDefaultServiceStampAction.test.js +++ b/web-client/src/presenter/actions/CourtIssuedDocketEntry/setDefaultServiceStampAction.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -9,7 +9,7 @@ presenter.providers.applicationContext = applicationContext; describe('setDefaultServiceStampAction', () => { it('should set default serviceStamp on form if user is a petitions clerk', async () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }); const result = await runAction(setDefaultServiceStampAction, { @@ -28,7 +28,7 @@ describe('setDefaultServiceStampAction', () => { it('should not set default serviceStamp on form if user is a docket clerk', async () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }); const result = await runAction(setDefaultServiceStampAction, { diff --git a/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.js b/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.js index d37c62a8822..fb4b87cfe8c 100644 --- a/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.js +++ b/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.js @@ -1,18 +1,4 @@ import { state } from 'cerebral'; -import orderTemplate from '../../../views/CreateOrder/orderTemplate.html'; - -const replaceWithID = (replacements, domString) => { - const parser = new window.DOMParser(); - const doc = parser.parseFromString(domString, 'text/html'); - - Object.keys(replacements).forEach(id => { - if (doc.querySelector(id)) { - doc.querySelector(id).innerHTML = replacements[id]; - } - }); - - return doc; -}; export const createOrderAction = async ({ applicationContext, get }) => { let richText = get(state.form.richText) || ''; @@ -21,40 +7,16 @@ export const createOrderAction = async ({ applicationContext, get }) => { /\t/g, '            ', ); - const caseDetail = get(state.caseDetail); - const caseCaption = caseDetail.caseCaption || ''; + const isOrderEvent = get(state.form.eventCode) == 'NOT'; // 'NOT' === 'notice' - let caseTitle = applicationContext.getCaseTitle(caseCaption); - let caseCaptionExtension = ''; - if (caseTitle !== caseCaption) { - caseTitle += ', '; - caseCaptionExtension = caseCaption.replace(caseTitle, ''); - } let signatureForNotice = ''; if (isOrderEvent) { - signatureForNotice = `

${applicationContext.getClerkOfCourtNameForSigning()}
Clerk of the Court

`; + signatureForNotice = applicationContext.getClerkOfCourtNameForSigning(); } - const { docketNumberWithSuffix } = caseDetail; - - const doc = replaceWithID( - { - '#caseCaptionExtension': caseCaptionExtension, - '#caseTitle': caseTitle, - '#docketNumber': docketNumberWithSuffix, - '#orderBody': richText, - '#orderTitleHeader': documentTitle, - '#signature': signatureForNotice, - }, - orderTemplate, - ); - - let result = doc.children[0].innerHTML; - - result = result.replace( - '/* STYLES_PLACEHOLDER */', - await applicationContext.getPdfStyles(), - ); - - return { htmlString: result }; + return { + contentHtml: richText, + documentTitle, + signatureText: signatureForNotice, + }; }; diff --git a/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.test.js b/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.test.js index cda4c6618da..c03633b8ace 100644 --- a/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.test.js +++ b/web-client/src/presenter/actions/CourtIssuedOrder/createOrderAction.test.js @@ -32,12 +32,16 @@ describe('createOrderAction', () => { caseDetail: { caseCaption: 'Guy Fieri', }, + form: { + documentTitle: 'Test Title', + richText: 'Foo', + }, }, }); - expect(applicationContextForClient.getCaseTitle).toBeCalled(); - expect(applicationContextForClient.getPdfStyles).toBeCalled(); - expect(result.output.htmlString.indexOf('Guy Fieri')).toBeTruthy(); + expect(result.output.contentHtml).toEqual('Foo'); + expect(result.output.documentTitle).toEqual('TEST TITLE'); + expect(result.output.signatureText).toEqual(''); }); it('creates an order for a notice', async () => { @@ -59,12 +63,11 @@ describe('createOrderAction', () => { }, }); - expect(applicationContextForClient.getCaseTitle).toBeCalled(); - expect(applicationContextForClient.getPdfStyles).toBeCalled(); expect( applicationContextForClient.getClerkOfCourtNameForSigning, ).toBeCalled(); - expect(result.output.htmlString.indexOf('Guy Fieri')).toBeTruthy(); - expect(result.output.htmlString.indexOf('Bobby Flay')).toBeTruthy(); + expect(result.output.contentHtml).toEqual(''); + expect(result.output.documentTitle).toEqual(''); + expect(result.output.signatureText).toEqual('Bobby Flay'); }); }); diff --git a/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.js b/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.js index 4636c5f30a5..fb13d242544 100644 --- a/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.js +++ b/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.js @@ -9,23 +9,19 @@ import { state } from 'cerebral'; * @returns {object} pdfUrl */ export const getPdfUrlAction = async ({ applicationContext, get, props }) => { - const { htmlString } = props; + const { contentHtml, documentTitle, signatureText } = props; const caseDetail = get(state.caseDetail); - if (!htmlString) { - throw new Error('No markup found in documentHtml'); - } - - const { docketNumberWithSuffix } = caseDetail; - const { url, } = await applicationContext .getUseCases() .createCourtIssuedOrderPdfFromHtmlInteractor({ applicationContext, - docketNumberWithSuffix, - htmlString, + caseId: caseDetail.caseId, + contentHtml, + documentTitle, + signatureText, }); return { pdfUrl: url }; diff --git a/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.test.js b/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.test.js index 511d83f4955..41ded54fb34 100644 --- a/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.test.js +++ b/web-client/src/presenter/actions/CourtIssuedOrder/getPdfUrlAction.test.js @@ -16,15 +16,6 @@ describe('getPdfUrlAction', () => { }; }); - it('throws error if htmlString is empty', async () => { - await expect( - runAction(getPdfUrlAction, { - props: { htmlString: '' }, - state: {}, - }), - ).rejects.toThrow(); - }); - it('gets the pdf file url for a court issued document', async () => { const mockPdf = { url: 'www.example.com' }; applicationContextForClient @@ -35,8 +26,16 @@ describe('getPdfUrlAction', () => { modules: { presenter, }, - props: { htmlString: '

hi

' }, - state: { caseDetail: {} }, + props: { + contentHtml: '

hi

', + documentTitle: 'Test Title', + signatureText: 'Test Signature', + }, + state: { + caseDetail: { + caseId: '123', + }, + }, }); expect( @@ -44,5 +43,17 @@ describe('getPdfUrlAction', () => { .createCourtIssuedOrderPdfFromHtmlInteractor, ).toBeCalled(); expect(result.output.pdfUrl).toBe(mockPdf.url); + + const args = applicationContextForClient.getUseCases() + .createCourtIssuedOrderPdfFromHtmlInteractor.mock.calls[0][0]; + + expect(args).toEqual( + expect.objectContaining({ + caseId: '123', + contentHtml: '

hi

', + documentTitle: 'Test Title', + signatureText: 'Test Signature', + }), + ); }); }); diff --git a/web-client/src/presenter/actions/CourtIssuedOrder/overwriteCorrespondenceFileAction.js b/web-client/src/presenter/actions/CourtIssuedOrder/overwriteCorrespondenceFileAction.js new file mode 100644 index 00000000000..7e62e9fd1e5 --- /dev/null +++ b/web-client/src/presenter/actions/CourtIssuedOrder/overwriteCorrespondenceFileAction.js @@ -0,0 +1,35 @@ +import { state } from 'cerebral'; + +/** + * upload correspondence to s3. + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {object} providers.path the cerebral path to take depending on if the correspondence file was uploaded successfully or not + * @param {object} providers.get the cerebral get method used for getting state + * @returns {object} the next path based on if the correspondence file was successfully uploaded or not + */ +export const overwriteCorrespondenceFileAction = async ({ + applicationContext, + get, + path, +}) => { + const { primaryDocumentFile } = get(state.form); + const documentToEdit = get(state.documentToEdit); + + try { + const primaryDocumentFileId = await applicationContext + .getUseCases() + .uploadCorrespondenceDocumentInteractor({ + applicationContext, + documentFile: primaryDocumentFile, + documentIdToOverwrite: documentToEdit.documentId, + }); + + return path.success({ + primaryDocumentFileId, + }); + } catch (err) { + return path.error(); + } +}; diff --git a/web-client/src/presenter/actions/CourtIssuedOrder/overwriteCorrespondenceFileAction.test.js b/web-client/src/presenter/actions/CourtIssuedOrder/overwriteCorrespondenceFileAction.test.js new file mode 100644 index 00000000000..5c0f9a6506c --- /dev/null +++ b/web-client/src/presenter/actions/CourtIssuedOrder/overwriteCorrespondenceFileAction.test.js @@ -0,0 +1,70 @@ +import { applicationContextForClient } from '../../../../../shared/src/business/test/createTestApplicationContext'; +import { overwriteCorrespondenceFileAction } from './overwriteCorrespondenceFileAction'; +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('overwriteCorrespondenceFileAction', () => { + const errorStub = jest.fn(); + const successStub = jest.fn(); + + beforeAll(() => { + presenter.providers.applicationContext = applicationContextForClient; + + presenter.providers.path = { + error: errorStub, + success: successStub, + }; + }); + + it('returns the success path with the documentId when the correspondence file was successfully uploaded', async () => { + applicationContextForClient + .getUseCases() + .uploadCorrespondenceDocumentInteractor.mockReturnValue( + 'document-id-123', + ); + + await runAction(overwriteCorrespondenceFileAction, { + modules: { + presenter, + }, + state: { + documentToEdit: { + documentId: 'document-id-123', + }, + form: { + primaryDocumentFile: {}, + }, + }, + }); + + expect( + applicationContextForClient.getUseCases() + .uploadCorrespondenceDocumentInteractor, + ).toBeCalled(); + expect(successStub).toHaveBeenCalledWith({ + primaryDocumentFileId: 'document-id-123', + }); + }); + + it('returns the error path when the correspondence file failed to upload', async () => { + applicationContextForClient + .getUseCases() + .uploadCorrespondenceDocumentInteractor.mockImplementation(() => { + throw new Error(); + }); + + runAction(overwriteCorrespondenceFileAction, { + modules: { presenter }, + state: { + documentToEdit: { + documentId: 'document-id-123', + }, + form: { + primaryDocumentFile: {}, + }, + }, + }); + + expect(errorStub).toBeCalled(); + }); +}); diff --git a/web-client/src/presenter/actions/Dashboard/incrementCurrentPageClosedCasesAction.js b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageClosedCasesAction.js new file mode 100644 index 00000000000..a4d0ffb2300 --- /dev/null +++ b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageClosedCasesAction.js @@ -0,0 +1,14 @@ +import { state } from 'cerebral'; + +/** + * increments state.closedCasesCurrentPage by 1 + * + * @param {object} providers the providers object + * @param {Function} providers.get the cerebral get function + * @param {Function} providers.store the cerebral store function + */ +export const incrementCurrentPageClosedCasesAction = ({ get, store }) => { + const currentPage = get(state.closedCasesCurrentPage) || 1; + + store.set(state.closedCasesCurrentPage, currentPage + 1); +}; diff --git a/web-client/src/presenter/actions/Dashboard/incrementCurrentPageClosedCasesAction.test.js b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageClosedCasesAction.test.js new file mode 100644 index 00000000000..dd4d1057846 --- /dev/null +++ b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageClosedCasesAction.test.js @@ -0,0 +1,14 @@ +import { incrementCurrentPageClosedCasesAction } from './incrementCurrentPageClosedCasesAction'; +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('incrementCurrentPageClosedCasesAction', () => { + it('increments state.closedCasesCurrentPage by 1', async () => { + const result = await runAction(incrementCurrentPageClosedCasesAction, { + modules: { presenter }, + state: { closedCasesCurrentPage: 2 }, + }); + + expect(result.state.closedCasesCurrentPage).toEqual(3); + }); +}); diff --git a/web-client/src/presenter/actions/Dashboard/incrementCurrentPageOpenCasesAction.js b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageOpenCasesAction.js new file mode 100644 index 00000000000..26196811c22 --- /dev/null +++ b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageOpenCasesAction.js @@ -0,0 +1,14 @@ +import { state } from 'cerebral'; + +/** + * increments state.openCasesCurrentPage by 1 + * + * @param {object} providers the providers object + * @param {Function} providers.get the cerebral get function + * @param {Function} providers.store the cerebral store function + */ +export const incrementCurrentPageOpenCasesAction = ({ get, store }) => { + const currentPage = get(state.openCasesCurrentPage) || 1; + + store.set(state.openCasesCurrentPage, currentPage + 1); +}; diff --git a/web-client/src/presenter/actions/Dashboard/incrementCurrentPageOpenCasesAction.test.js b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageOpenCasesAction.test.js new file mode 100644 index 00000000000..c9697c07c7f --- /dev/null +++ b/web-client/src/presenter/actions/Dashboard/incrementCurrentPageOpenCasesAction.test.js @@ -0,0 +1,14 @@ +import { incrementCurrentPageOpenCasesAction } from './incrementCurrentPageOpenCasesAction'; +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('incrementCurrentPageOpenCasesAction', () => { + it('increments state.openCasesCurrentPage by 1', async () => { + const result = await runAction(incrementCurrentPageOpenCasesAction, { + modules: { presenter }, + state: { openCasesCurrentPage: 2 }, + }); + + expect(result.state.openCasesCurrentPage).toEqual(3); + }); +}); diff --git a/web-client/src/presenter/actions/DocketEntry/submitDocketEntryAction.js b/web-client/src/presenter/actions/DocketEntry/submitDocketEntryAction.js deleted file mode 100644 index 55d79fc8888..00000000000 --- a/web-client/src/presenter/actions/DocketEntry/submitDocketEntryAction.js +++ /dev/null @@ -1,85 +0,0 @@ -import { omit } from 'lodash'; -import { state } from 'cerebral'; - -/** - * submit a new docket entry - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {object} providers.props the cerebral props object - * @returns {Promise} async action - */ -export const submitDocketEntryAction = async ({ - applicationContext, - get, - props, -}) => { - const { caseId, docketNumber } = get(state.caseDetail); - const documentId = get(state.documentId); - const { primaryDocumentFileId } = props; - - const isFileAttached = !!primaryDocumentFileId; - const isEditing = get(state.isEditingDocketEntry); - - let documentMetadata = omit( - { - ...get(state.form), - }, - ['primaryDocumentFile'], - ); - - documentMetadata = { - ...documentMetadata, - caseId, - createdAt: applicationContext.getUtilities().createISODateString(), - docketNumber, - isFileAttached, - isPaper: true, - receivedAt: documentMetadata.dateReceived, - }; - - if (isFileAttached) { - await applicationContext.getUseCases().virusScanPdfInteractor({ - applicationContext, - documentId: primaryDocumentFileId, - }); - await applicationContext.getUseCases().validatePdfInteractor({ - applicationContext, - documentId: primaryDocumentFileId, - }); - } - - let caseDetail; - - if (isEditing) { - caseDetail = await applicationContext - .getUseCases() - .updateDocketEntryInteractor({ - applicationContext, - documentMetadata, - primaryDocumentFileId: documentId, - }); - } else { - caseDetail = await applicationContext - .getUseCases() - .fileDocketEntryInteractor({ - applicationContext, - documentMetadata, - primaryDocumentFileId: - primaryDocumentFileId || applicationContext.getUniqueId(), - }); - } - - if (isFileAttached) { - await applicationContext.getUseCases().addCoversheetInteractor({ - applicationContext, - caseId: caseDetail.caseId, - documentId: primaryDocumentFileId, - }); - } - - return { - caseDetail, - caseId: docketNumber, - }; -}; diff --git a/web-client/src/presenter/actions/DocketEntry/submitDocketEntryAction.test.js b/web-client/src/presenter/actions/DocketEntry/submitDocketEntryAction.test.js deleted file mode 100644 index d8e9c41138e..00000000000 --- a/web-client/src/presenter/actions/DocketEntry/submitDocketEntryAction.test.js +++ /dev/null @@ -1,98 +0,0 @@ -import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { submitDocketEntryAction } from './submitDocketEntryAction'; - -presenter.providers.applicationContext = applicationContext; - -describe('submitDocketEntryAction', () => { - it('should call fileDocketEntry', async () => { - applicationContext - .getUseCases() - .fileDocketEntryInteractor.mockReturnValue({ documents: [] }); - - await runAction(submitDocketEntryAction, { - modules: { - presenter, - }, - state: { - caseDetail: {}, - form: { - primaryDocumentFile: {}, - }, - }, - }); - - expect( - applicationContext.getUseCases().fileDocketEntryInteractor.mock.calls - .length, - ).toEqual(1); - }); - - it('should call virusScan and validation and if a file is attached', async () => { - applicationContext.getUseCases().fileDocketEntryInteractor.mockReturnValue({ - caseId: applicationContext.getUniqueId(), - }); - - await runAction(submitDocketEntryAction, { - modules: { - presenter, - }, - props: { - primaryDocumentFileId: applicationContext.getUniqueId(), - }, - state: { - caseDetail: { - caseId: applicationContext.getUniqueId(), - }, - form: { - primaryDocumentFile: {}, - }, - }, - }); - - expect( - applicationContext.getUseCases().validatePdfInteractor.mock.calls.length, - ).toEqual(1); - expect( - applicationContext.getUseCases().virusScanPdfInteractor.mock.calls.length, - ).toEqual(1); - }); - - it('should update docket entry with attached file', async () => { - applicationContext - .getUseCases() - .updateDocketEntryInteractor.mockReturnValue({ - caseId: applicationContext.getUniqueId(), - }); - - await runAction(submitDocketEntryAction, { - modules: { - presenter, - }, - props: { - primaryDocumentFileId: applicationContext.getUniqueId(), - }, - state: { - caseDetail: { - caseId: applicationContext.getUniqueId(), - }, - form: { - primaryDocumentFile: {}, - }, - isEditingDocketEntry: true, - }, - }); - - expect( - applicationContext.getUseCases().validatePdfInteractor.mock.calls.length, - ).toEqual(1); - expect( - applicationContext.getUseCases().virusScanPdfInteractor.mock.calls.length, - ).toEqual(1); - expect( - applicationContext.getUseCases().updateDocketEntryInteractor.mock.calls - .length, - ).toEqual(1); - }); -}); diff --git a/web-client/src/presenter/actions/DocketEntry/updateDocketEntryWithFileAction.js b/web-client/src/presenter/actions/DocketEntry/updateDocketEntryWithFileAction.js index 8bfc9a43872..e9a99515a7a 100644 --- a/web-client/src/presenter/actions/DocketEntry/updateDocketEntryWithFileAction.js +++ b/web-client/src/presenter/actions/DocketEntry/updateDocketEntryWithFileAction.js @@ -43,6 +43,12 @@ export const updateDocketEntryWithFileAction = async ({ documentId, }); + await applicationContext.getUseCases().addCoversheetInteractor({ + applicationContext, + caseId, + documentId, + }); + const caseDetail = await applicationContext .getUseCases() .updateDocketEntryInteractor({ @@ -51,12 +57,6 @@ export const updateDocketEntryWithFileAction = async ({ primaryDocumentFileId: documentId, }); - await applicationContext.getUseCases().addCoversheetInteractor({ - applicationContext, - caseId: caseDetail.caseId, - documentId, - }); - return { caseDetail, caseId: docketNumber, diff --git a/web-client/src/presenter/actions/DocketEntry/updateDocketEntryWithFileAction.test.js b/web-client/src/presenter/actions/DocketEntry/updateDocketEntryWithFileAction.test.js new file mode 100644 index 00000000000..7b1563f38ae --- /dev/null +++ b/web-client/src/presenter/actions/DocketEntry/updateDocketEntryWithFileAction.test.js @@ -0,0 +1,42 @@ +import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { updateDocketEntryWithFileAction } from './updateDocketEntryWithFileAction'; + +describe('updateDocketEntryWithFileAction', () => { + const mockCaseDetail = { + caseId: '123', + docketNumber: '123-45', + }; + + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + + applicationContext + .getUseCases() + .updateDocketEntryInteractor.mockReturnValue(mockCaseDetail); + }); + + it('should call updateDocketEntryInteractor and return caseDetail', async () => { + const result = await runAction(updateDocketEntryWithFileAction, { + modules: { + presenter, + }, + state: { + caseDetail: mockCaseDetail, + document: '123-456-789-abc', + form: { + primaryDocumentFile: {}, + }, + }, + }); + + expect( + applicationContext.getUseCases().updateDocketEntryInteractor, + ).toHaveBeenCalled(); + expect(result.output).toEqual({ + caseDetail: mockCaseDetail, + caseId: mockCaseDetail.docketNumber, + }); + }); +}); diff --git a/web-client/src/presenter/actions/EditDocketRecordEntry/setDocketEntryMetaTypeAction.test.js b/web-client/src/presenter/actions/EditDocketRecordEntry/setDocketEntryMetaTypeAction.test.js index 89c9fdc471f..c1f8e7d218d 100644 --- a/web-client/src/presenter/actions/EditDocketRecordEntry/setDocketEntryMetaTypeAction.test.js +++ b/web-client/src/presenter/actions/EditDocketRecordEntry/setDocketEntryMetaTypeAction.test.js @@ -1,4 +1,4 @@ -import { Document } from '../../../../../shared/src/business/entities/Document'; +import { COURT_ISSUED_EVENT_CODES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -13,7 +13,7 @@ describe('setDocketEntryMetaTypeAction', () => { state: { form: { documentId: '123', - eventCode: Document.COURT_ISSUED_EVENT_CODES[0].eventCode, + eventCode: COURT_ISSUED_EVENT_CODES[0].eventCode, }, }, }); diff --git a/web-client/src/presenter/actions/FileDocument/clearDocumentScenarioAction.js b/web-client/src/presenter/actions/FileDocument/clearDocumentScenarioAction.js deleted file mode 100644 index a4990d7cc4d..00000000000 --- a/web-client/src/presenter/actions/FileDocument/clearDocumentScenarioAction.js +++ /dev/null @@ -1,11 +0,0 @@ -import { state } from 'cerebral'; - -/** - * Clears document scenario. - * - * @param {object} providers the providers object - * @param {object} providers.store the cerebral store object used for clearing scenario - */ -export const clearDocumentScenarioAction = ({ store }) => { - store.unset(state.form.scenario); -}; diff --git a/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentFormAction.js b/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentFormAction.js deleted file mode 100644 index 26dfe9bff8a..00000000000 --- a/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentFormAction.js +++ /dev/null @@ -1,11 +0,0 @@ -import { state } from 'cerebral'; - -/** - * Clears secondary document. - * - * @param {object} providers the providers object - * @param {object} providers.store the cerebral store object used for clearing secondaryDocument - */ -export const clearSecondaryDocumentFormAction = ({ store }) => { - store.unset(state.form.secondaryDocument); -}; diff --git a/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentScenarioAction.js b/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentScenarioAction.js deleted file mode 100644 index 852e217dc0c..00000000000 --- a/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentScenarioAction.js +++ /dev/null @@ -1,11 +0,0 @@ -import { state } from 'cerebral'; - -/** - * Clears secondary document scenario. - * - * @param {object} providers the providers object - * @param {object} providers.store the cerebral store object used for clearing secondaryDocument.scenario - */ -export const clearSecondaryDocumentScenarioAction = ({ store }) => { - store.unset(state.form.secondaryDocument.scenario); -}; diff --git a/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentScenarioAction.test.js b/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentScenarioAction.test.js deleted file mode 100644 index bc3530ca46e..00000000000 --- a/web-client/src/presenter/actions/FileDocument/clearSecondaryDocumentScenarioAction.test.js +++ /dev/null @@ -1,18 +0,0 @@ -import { clearSecondaryDocumentScenarioAction } from './clearSecondaryDocumentScenarioAction'; -import { runAction } from 'cerebral/test'; - -describe('clearSecondaryDocumentScenarioAction', () => { - it('clears the secondaryDocument.scenario within the form', async () => { - const result = await runAction(clearSecondaryDocumentScenarioAction, { - state: { - form: { - secondaryDocument: { - scenario: {}, - }, - }, - }, - }); - - expect(result.state.form.secondaryDocument.scenario).toBeUndefined(); - }); -}); diff --git a/web-client/src/presenter/actions/FileDocument/clearWizardDataAction.test.js b/web-client/src/presenter/actions/FileDocument/clearWizardDataAction.test.js index ccc86846b01..b0d9b51e6f6 100644 --- a/web-client/src/presenter/actions/FileDocument/clearWizardDataAction.test.js +++ b/web-client/src/presenter/actions/FileDocument/clearWizardDataAction.test.js @@ -1,7 +1,7 @@ import { clearWizardDataAction } from './clearWizardDataAction'; import { runAction } from 'cerebral/test'; -describe('clearSecondaryDocumentScenarioAction', () => { +describe('clearWizardDataAction', () => { it('clears document scenario with "category" key', async () => { const result = await runAction(clearWizardDataAction, { props: { diff --git a/web-client/src/presenter/actions/FileDocument/primeDoNotProceedPropAction.js b/web-client/src/presenter/actions/FileDocument/primeDoNotProceedPropAction.js deleted file mode 100644 index bfa994187ad..00000000000 --- a/web-client/src/presenter/actions/FileDocument/primeDoNotProceedPropAction.js +++ /dev/null @@ -1,23 +0,0 @@ -import { state } from 'cerebral'; - -/** - * Set docket number as prop. To allow for routing. - * - * @param {object} providers the providers object - * @param {object} providers.get the cerebral get function - * - * @returns {object} the docketNumber prop - */ -export const primeDoNotProceedPropAction = ({ get }) => { - const { scenario } = get(state.form); - const { isDocumentTypeSelected, isSecondaryDocumentTypeSelected } = get( - state.screenMetadata, - ); - - const isSecondaryDocumentNeeded = scenario === 'Nonstandard H'; - return { - doNotProceed: - !isDocumentTypeSelected || - (isSecondaryDocumentNeeded && !isSecondaryDocumentTypeSelected), - }; -}; diff --git a/web-client/src/presenter/actions/FileDocument/primeDoNotProceedPropAction.test.js b/web-client/src/presenter/actions/FileDocument/primeDoNotProceedPropAction.test.js deleted file mode 100644 index 359f56ff253..00000000000 --- a/web-client/src/presenter/actions/FileDocument/primeDoNotProceedPropAction.test.js +++ /dev/null @@ -1,64 +0,0 @@ -import { primeDoNotProceedPropAction } from './primeDoNotProceedPropAction'; -import { runAction } from 'cerebral/test'; - -describe('primeDoNotProceedPropAction', () => { - it('should set doNotProceed to TRUE if there is no document type selected', async () => { - const result = await runAction(primeDoNotProceedPropAction, { - state: { - form: {}, - screenMetadata: { - isDocumentTypeSelected: false, - }, - }, - }); - - expect(result.output.doNotProceed).toBeTruthy(); - }); - - it('should set doNotProceed to TRUE if a secondary document is needed and no secondary type is selected for a Nonstandard H scenario', async () => { - const result = await runAction(primeDoNotProceedPropAction, { - state: { - form: { - scenario: 'Nonstandard H', - }, - screenMetadata: { - isSecondaryDocumentTypeSelected: false, - }, - }, - }); - - expect(result.output.doNotProceed).toBeTruthy(); - }); - - it('should set doNotProceed to FALSE if a secondary document is needed, no secondary type is selected for a different scenario', async () => { - const result = await runAction(primeDoNotProceedPropAction, { - state: { - form: { - scenario: 'Nonstandard H', - }, - screenMetadata: { - isDocumentTypeSelected: true, - isSecondaryDocumentTypeSelected: true, - }, - }, - }); - - expect(result.output.doNotProceed).toBeFalsy(); - }); - - it('should set doNotProceed to FALSE if a secondary document is not needed and no secondary type is selected', async () => { - const result = await runAction(primeDoNotProceedPropAction, { - state: { - form: { - scenario: 'Nonstandard', - }, - screenMetadata: { - isDocumentTypeSelected: true, - isSecondaryDocumentTypeSelected: false, - }, - }, - }); - - expect(result.output.doNotProceed).toBeFalsy(); - }); -}); diff --git a/web-client/src/presenter/actions/FileDocument/setDefaultFileDocumentFormValuesAction.test.js b/web-client/src/presenter/actions/FileDocument/setDefaultFileDocumentFormValuesAction.test.js index ebbe411c2e1..2a8eb1054e8 100644 --- a/web-client/src/presenter/actions/FileDocument/setDefaultFileDocumentFormValuesAction.test.js +++ b/web-client/src/presenter/actions/FileDocument/setDefaultFileDocumentFormValuesAction.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -9,7 +9,7 @@ describe('setDefaultFileDocumentFormValuesAction', () => { it('sets form.partyPrimary to true if the user is a petitioner', async () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); const result = await runAction(setDefaultFileDocumentFormValuesAction, { modules: { presenter }, @@ -24,7 +24,7 @@ describe('setDefaultFileDocumentFormValuesAction', () => { }); it('does not set form.partyPrimary if the user is not a petitioner', async () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); const result = await runAction(setDefaultFileDocumentFormValuesAction, { modules: { presenter }, diff --git a/web-client/src/presenter/actions/FileDocument/setDocumentScenarioAction.js b/web-client/src/presenter/actions/FileDocument/setDocumentScenarioAction.js deleted file mode 100644 index 76ca962fba3..00000000000 --- a/web-client/src/presenter/actions/FileDocument/setDocumentScenarioAction.js +++ /dev/null @@ -1,27 +0,0 @@ -import { state } from 'cerebral'; - -/** - * Set document scenario. - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {Function} providers.get the cerebral get function - * @param {object} providers.store the cerebral store object - * @returns {undefined} - */ -export const setDocumentScenarioAction = ({ - applicationContext, - get, - store, -}) => { - const { category, documentType } = get(state.form); - const { CATEGORY_MAP } = applicationContext.getConstants(); - - const categoryInformation = CATEGORY_MAP[category].find( - itemDocumentType => itemDocumentType.documentType === documentType, - ); - - store.set(state.form.scenario, categoryInformation.scenario); - store.set(state.form.documentTitle, categoryInformation.documentTitle); - store.set(state.form.eventCode, categoryInformation.eventCode); -}; diff --git a/web-client/src/presenter/actions/FileDocument/setDocumentScenarioAction.test.js b/web-client/src/presenter/actions/FileDocument/setDocumentScenarioAction.test.js deleted file mode 100644 index 09e6e9dd41e..00000000000 --- a/web-client/src/presenter/actions/FileDocument/setDocumentScenarioAction.test.js +++ /dev/null @@ -1,37 +0,0 @@ -import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { setDocumentScenarioAction } from './setDocumentScenarioAction'; - -describe('setDocumentScenarioAction', () => { - presenter.providers.applicationContext = applicationContext; - - it('sets document scenario', async () => { - const documentScenario = { - category: 'Notice', - documentTitle: 'Notice of Abatement of Jeopardy Assessment', - documentType: 'Notice of Abatement of Jeopardy Assessment', - eventCode: 'NAJA', - labelFreeText: '', - labelPreviousDocument: '', - ordinalField: '', - scenario: 'Standard', - }; - - const result = await runAction(setDocumentScenarioAction, { - modules: { presenter }, - state: { - form: { - category: 'Notice', - documentType: 'Notice of Abatement of Jeopardy Assessment', - }, - }, - }); - - expect(result.state.form.scenario).toEqual(documentScenario.scenario); - expect(result.state.form.documentTitle).toEqual( - documentScenario.documentTitle, - ); - expect(result.state.form.eventCode).toEqual(documentScenario.eventCode); - }); -}); diff --git a/web-client/src/presenter/actions/FileDocument/setSecondaryDocumentScenarioAction.js b/web-client/src/presenter/actions/FileDocument/setSecondaryDocumentScenarioAction.js deleted file mode 100644 index 7a7f506b7cd..00000000000 --- a/web-client/src/presenter/actions/FileDocument/setSecondaryDocumentScenarioAction.js +++ /dev/null @@ -1,40 +0,0 @@ -import { state } from 'cerebral'; - -/** - * Set secondary document scenario. - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context - * @param {Function} providers.get the cerebral get function - * @param {object} providers.store the cerebral store object - * @returns {undefined} - */ -export const setSecondaryDocumentScenarioAction = ({ - applicationContext, - get, - store, -}) => { - const secondaryDocument = get(state.form.secondaryDocument); - - if (secondaryDocument && secondaryDocument.documentType) { - const { category, documentType } = secondaryDocument; - const { CATEGORY_MAP } = applicationContext.getConstants(); - - const categoryInformation = CATEGORY_MAP[category].find( - itemDocumentType => itemDocumentType.documentType === documentType, - ); - - store.set( - state.form.secondaryDocument.scenario, - categoryInformation.scenario, - ); - store.set( - state.form.secondaryDocument.documentTitle, - categoryInformation.documentTitle, - ); - store.set( - state.form.secondaryDocument.eventCode, - categoryInformation.eventCode, - ); - } -}; diff --git a/web-client/src/presenter/actions/FileDocument/setSecondaryDocumentScenarioAction.test.js b/web-client/src/presenter/actions/FileDocument/setSecondaryDocumentScenarioAction.test.js deleted file mode 100644 index 63fcaeb9e83..00000000000 --- a/web-client/src/presenter/actions/FileDocument/setSecondaryDocumentScenarioAction.test.js +++ /dev/null @@ -1,60 +0,0 @@ -import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { setSecondaryDocumentScenarioAction } from './setSecondaryDocumentScenarioAction'; - -describe('setSecondaryDocumentScenarioAction', () => { - presenter.providers.applicationContext = applicationContext; - - it('sets secondaryDocument scenario', async () => { - const documentScenario = { - category: 'Notice', - documentTitle: 'Notice of Abatement of Jeopardy Assessment', - documentType: 'Notice of Abatement of Jeopardy Assessment', - eventCode: 'NAJA', - labelFreeText: '', - labelPreviousDocument: '', - ordinalField: '', - scenario: 'Standard', - }; - - const result = await runAction(setSecondaryDocumentScenarioAction, { - modules: { presenter }, - state: { - form: { - secondaryDocument: { - category: 'Notice', - documentType: 'Notice of Abatement of Jeopardy Assessment', - }, - }, - }, - }); - - expect(result.state.form.secondaryDocument.scenario).toEqual( - documentScenario.scenario, - ); - expect(result.state.form.secondaryDocument.documentTitle).toEqual( - documentScenario.documentTitle, - ); - expect(result.state.form.secondaryDocument.eventCode).toEqual( - documentScenario.eventCode, - ); - }); - - it('does not set secondaryDocument scenario', async () => { - const result = await runAction(setSecondaryDocumentScenarioAction, { - modules: { presenter }, - state: { - form: { - secondaryDocument: { - category: 'Notice', - }, - }, - }, - }); - - expect(result.state.form.secondaryDocument.scenario).toBeUndefined(); - expect(result.state.form.secondaryDocument.documentTitle).toBeUndefined(); - expect(result.state.form.secondaryDocument.eventCode).toBeUndefined(); - }); -}); diff --git a/web-client/src/presenter/actions/FileDocument/shouldProceedAction.js b/web-client/src/presenter/actions/FileDocument/shouldProceedAction.js deleted file mode 100644 index 2fda2f3d08c..00000000000 --- a/web-client/src/presenter/actions/FileDocument/shouldProceedAction.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * allows you to skip path based on props - * - * @param {object} providers the providers object - * @param {object} providers.path the next object in the path - * @param {object} providers.props the cerebral props that contain the props.doNotProceed - * @returns {object} the results of the executed path - */ -export const shouldProceedAction = ({ path, props }) => { - if (props.doNotProceed) return path.ignore(); - return path.proceed(); -}; diff --git a/web-client/src/presenter/actions/FileDocument/shouldProceedAction.test.js b/web-client/src/presenter/actions/FileDocument/shouldProceedAction.test.js deleted file mode 100644 index a95bc7a5210..00000000000 --- a/web-client/src/presenter/actions/FileDocument/shouldProceedAction.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { shouldProceedAction } from './shouldProceedAction'; - -describe('shouldProceedAction', () => { - let proceedStub; - let ignoreStub; - - beforeAll(() => { - proceedStub = jest.fn(); - ignoreStub = jest.fn(); - - presenter.providers.path = { - ignore: ignoreStub, - proceed: proceedStub, - }; - }); - - it('should return proceed if props.doNotProceed is FALSE', async () => { - await runAction(shouldProceedAction, { - modules: { - presenter, - }, - props: { - doNotProceed: false, - }, - }); - - expect(proceedStub).toHaveBeenCalled(); - }); - - it('should return ignore if props.doNotProceed is TRUE', async () => { - await runAction(shouldProceedAction, { - modules: { - presenter, - }, - props: { - doNotProceed: true, - }, - }); - - expect(ignoreStub).toHaveBeenCalled(); - }); -}); diff --git a/web-client/src/presenter/actions/FileDocument/submitCaseAssociationRequestAction.test.js b/web-client/src/presenter/actions/FileDocument/submitCaseAssociationRequestAction.test.js index 6813a699231..5793d8b3ea0 100644 --- a/web-client/src/presenter/actions/FileDocument/submitCaseAssociationRequestAction.test.js +++ b/web-client/src/presenter/actions/FileDocument/submitCaseAssociationRequestAction.test.js @@ -1,3 +1,4 @@ +import { ROLES } from '../../../../../shared/src/business/entities/EntityConstants'; import { User } from '../../../../../shared/src/business/entities/User'; import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; @@ -18,7 +19,7 @@ describe('submitCaseAssociationRequestAction', () => { new User({ email: 'practitioner1@example.com', name: 'richard', - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'a805d1ab-18d0-43ec-bafb-654e83405416', }), ); diff --git a/web-client/src/presenter/actions/ManualAssociation/associateIrsPractitionerWithCaseAction.test.js b/web-client/src/presenter/actions/ManualAssociation/associateIrsPractitionerWithCaseAction.test.js index 42ffe56ae36..ba5579908a9 100644 --- a/web-client/src/presenter/actions/ManualAssociation/associateIrsPractitionerWithCaseAction.test.js +++ b/web-client/src/presenter/actions/ManualAssociation/associateIrsPractitionerWithCaseAction.test.js @@ -1,4 +1,4 @@ -import { SERVICE_INDICATOR_TYPES } from '../../../../../shared/src/business/entities/cases/CaseConstants'; +import { SERVICE_INDICATOR_TYPES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { associateIrsPractitionerWithCaseAction } from './associateIrsPractitionerWithCaseAction'; import { presenter } from '../../presenter-mock'; diff --git a/web-client/src/presenter/actions/PendingItems/isGlobalReportAction.js b/web-client/src/presenter/actions/PendingItems/isGlobalReportAction.js deleted file mode 100644 index cfd10fddf9b..00000000000 --- a/web-client/src/presenter/actions/PendingItems/isGlobalReportAction.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * changes the path based on if the report is global - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context used for accessing local storage - * @param {object} providers.path the cerebral path which contains the next path in the sequence (path of success or failure) - * @returns {object} the path the user should take - */ -export const isGlobalReportAction = async ({ path, props }) => { - const { caseIdFilter } = props; - - if (caseIdFilter) { - return path.no(); - } else { - return path.yes(); - } -}; diff --git a/web-client/src/presenter/actions/PendingItems/isGlobalReportAction.test.js b/web-client/src/presenter/actions/PendingItems/isGlobalReportAction.test.js deleted file mode 100644 index beb48ad42c1..00000000000 --- a/web-client/src/presenter/actions/PendingItems/isGlobalReportAction.test.js +++ /dev/null @@ -1,39 +0,0 @@ -import { isGlobalReportAction } from './isGlobalReportAction'; -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; - -let yesStub; -let noStub; - -describe('isGlobalReportAction', () => { - beforeAll(() => { - yesStub = jest.fn(); - noStub = jest.fn(); - - presenter.providers.path = { no: noStub, yes: yesStub }; - }); - - it('takes yes path when respondent is already in case', async () => { - await runAction(isGlobalReportAction, { - modules: { - presenter, - }, - props: {}, - }); - - expect(yesStub.mock.calls.length).toEqual(1); - }); - - it('takes no path when respondent is not already in case', async () => { - await runAction(isGlobalReportAction, { - modules: { - presenter, - }, - props: { - caseIdFilter: 'abc', - }, - }); - - expect(noStub.mock.calls.length).toEqual(1); - }); -}); diff --git a/web-client/src/presenter/actions/Public/getPublicJudgesAction.test.js b/web-client/src/presenter/actions/Public/getPublicJudgesAction.test.js index d02786d1fc3..c3deac65a61 100644 --- a/web-client/src/presenter/actions/Public/getPublicJudgesAction.test.js +++ b/web-client/src/presenter/actions/Public/getPublicJudgesAction.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { getPublicJudgesAction } from './getPublicJudgesAction'; import { presenter } from '../../presenter-public'; @@ -13,11 +13,11 @@ describe('getPublicJudgesAction', () => { const mockJudges = [ { name: 'Test Judge', - role: User.ROLES.judge, + role: ROLES.judge, }, { name: 'Test Judge2', - role: User.ROLES.judge, + role: ROLES.judge, }, ]; applicationContext diff --git a/web-client/src/presenter/actions/Public/getTodaysOpinionsAction.js b/web-client/src/presenter/actions/Public/getTodaysOpinionsAction.js new file mode 100644 index 00000000000..26f1a56ac1e --- /dev/null +++ b/web-client/src/presenter/actions/Public/getTodaysOpinionsAction.js @@ -0,0 +1,14 @@ +/** + * gets today's opinions + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the applicationContext + * @returns {Promise} a list of today's opinion documents + */ +export const getTodaysOpinionsAction = async ({ applicationContext }) => { + const todaysOpinions = await applicationContext + .getUseCases() + .getTodaysOpinionsInteractor({ applicationContext }); + + return { todaysOpinions }; +}; diff --git a/web-client/src/presenter/actions/Public/getTodaysOpinionsAction.test.js b/web-client/src/presenter/actions/Public/getTodaysOpinionsAction.test.js new file mode 100644 index 00000000000..47996ade8a9 --- /dev/null +++ b/web-client/src/presenter/actions/Public/getTodaysOpinionsAction.test.js @@ -0,0 +1,34 @@ +import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; +import { getTodaysOpinionsAction } from './getTodaysOpinionsAction'; +import { presenter } from '../../presenter-public'; +import { runAction } from 'cerebral/test'; + +describe('getTodaysOpinionsAction', () => { + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + }); + + it('gets the list of todays opinion', async () => { + const mockTodaysOpinions = [ + { + documentId: '1234', + documentTitle: 'An opinion', + }, + { + documentId: '5678', + documentTitle: 'Another opinion', + }, + ]; + applicationContext + .getUseCases() + .getTodaysOpinionsInteractor.mockReturnValue(mockTodaysOpinions); + + const result = await runAction(getTodaysOpinionsAction, { + modules: { + presenter, + }, + }); + + expect(result.output.todaysOpinions).toMatchObject(mockTodaysOpinions); + }); +}); diff --git a/web-client/src/presenter/actions/Public/setTodaysOpinionsAction.js b/web-client/src/presenter/actions/Public/setTodaysOpinionsAction.js new file mode 100644 index 00000000000..4cbf435f544 --- /dev/null +++ b/web-client/src/presenter/actions/Public/setTodaysOpinionsAction.js @@ -0,0 +1,10 @@ +import { state } from 'cerebral'; +/** + * sets the state.todaysOpinions based on props.todaysOpinions + * + * @param {object} props the props object + * @param {object} store the store object + */ +export const setTodaysOpinionsAction = async ({ props, store }) => { + store.set(state.todaysOpinions, props.todaysOpinions); +}; diff --git a/web-client/src/presenter/actions/Public/setTodaysOpinionsAction.test.js b/web-client/src/presenter/actions/Public/setTodaysOpinionsAction.test.js new file mode 100644 index 00000000000..7d7b4227704 --- /dev/null +++ b/web-client/src/presenter/actions/Public/setTodaysOpinionsAction.test.js @@ -0,0 +1,28 @@ +import { runAction } from 'cerebral/test'; +import { setTodaysOpinionsAction } from './setTodaysOpinionsAction'; + +describe('setTodaysOpinionsAction', () => { + it('sets state.todaysOpions from props.todaysOpinions', async () => { + const mockTodaysOpinions = [ + { + documentId: '1234', + documentTitle: 'An opinion', + }, + { + documentId: '5678', + documentTitle: 'Another opinion', + }, + ]; + + const { state } = await runAction(setTodaysOpinionsAction, { + props: { + todaysOpinions: mockTodaysOpinions, + }, + state: { + todaysOpinions: [], + }, + }); + + expect(state.todaysOpinions).toMatchObject(mockTodaysOpinions); + }); +}); diff --git a/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.js b/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.js index ffdd368512b..a8676dc7e23 100644 --- a/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.js +++ b/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.js @@ -1,4 +1,6 @@ +import { clone } from 'lodash'; import { state } from 'cerebral'; +import { trimDocketNumberSearch } from '../setCaseIdFromSearchAction'; /** * submit public opinion advanced search form @@ -12,13 +14,19 @@ export const submitPublicOpinionAdvancedSearchAction = async ({ applicationContext, get, }) => { - const form = get(state.advancedSearchForm.opinionSearch); + const searchParams = clone(get(state.advancedSearchForm.opinionSearch)); + + if (searchParams.docketNumber) { + searchParams.docketNumber = trimDocketNumberSearch( + searchParams.docketNumber, + ); + } const searchResults = await applicationContext .getUseCases() .opinionPublicSearchInteractor({ applicationContext, - searchParams: form, + searchParams, }); return { searchResults }; diff --git a/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.test.js b/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.test.js index a654ff614f3..686f9d05fde 100644 --- a/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.test.js +++ b/web-client/src/presenter/actions/Public/submitPublicOpinionAdvancedSearchAction.test.js @@ -34,4 +34,34 @@ describe('submitPublicOpinionAdvancedSearchAction', () => { }, }); }); + + it('should remove the docketNumberSuffix when a docket number is present', async () => { + await runAction(submitPublicOpinionAdvancedSearchAction, { + modules: { + presenter, + }, + state: { + advancedSearchForm: { + opinionSearch: { + docketNumber: '105-20L', + keyword: 'a', + }, + }, + }, + }); + + expect( + applicationContext.getUseCases().opinionPublicSearchInteractor.mock.calls + .length, + ).toEqual(1); + expect( + applicationContext.getUseCases().opinionPublicSearchInteractor.mock + .calls[0][0], + ).toMatchObject({ + searchParams: { + docketNumber: '105-20', + keyword: 'a', + }, + }); + }); }); diff --git a/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.js b/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.js index f8ce9879a92..083a1c07c51 100644 --- a/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.js +++ b/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.js @@ -1,4 +1,6 @@ +import { clone } from 'lodash'; import { state } from 'cerebral'; +import { trimDocketNumberSearch } from '../setCaseIdFromSearchAction'; /** * submit advanced search form @@ -12,7 +14,13 @@ export const submitPublicOrderAdvancedSearchAction = async ({ applicationContext, get, }) => { - const searchParams = get(state.advancedSearchForm.orderSearch); + const searchParams = clone(get(state.advancedSearchForm.orderSearch)); + + if (searchParams.docketNumber) { + searchParams.docketNumber = trimDocketNumberSearch( + searchParams.docketNumber, + ); + } const searchResults = await applicationContext .getUseCases() diff --git a/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.test.js b/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.test.js index 947f57dc5c6..0dbe72f5ccc 100644 --- a/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.test.js +++ b/web-client/src/presenter/actions/Public/submitPublicOrderAdvancedSearchAction.test.js @@ -34,4 +34,34 @@ describe('submitPublicOrderAdvancedSearchAction', () => { }, }); }); + + it('should remove the docketNumberSuffix when a docket number is present', async () => { + await runAction(submitPublicOrderAdvancedSearchAction, { + modules: { + presenter, + }, + state: { + advancedSearchForm: { + orderSearch: { + docketNumber: '105-20L', + keyword: 'a', + }, + }, + }, + }); + + expect( + applicationContext.getUseCases().orderPublicSearchInteractor.mock.calls + .length, + ).toEqual(1); + expect( + applicationContext.getUseCases().orderPublicSearchInteractor.mock + .calls[0][0], + ).toMatchObject({ + searchParams: { + docketNumber: '105-20', + keyword: 'a', + }, + }); + }); }); diff --git a/web-client/src/presenter/actions/StartCase/updatePartyTypeAction.test.js b/web-client/src/presenter/actions/StartCase/updatePartyTypeAction.test.js index 1caffda0455..d359219f7d6 100644 --- a/web-client/src/presenter/actions/StartCase/updatePartyTypeAction.test.js +++ b/web-client/src/presenter/actions/StartCase/updatePartyTypeAction.test.js @@ -1,5 +1,8 @@ -import { ContactFactory } from '../../../../../shared/src/business/entities/contacts/ContactFactory'; -import { User } from '../../../../../shared/src/business/entities/User'; +import { + COUNTRY_TYPES, + PARTY_TYPES, + ROLES, +} from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -17,7 +20,7 @@ const getFixtures = (props, state = {}) => ({ state: { ...state, user: { - role: User.ROLES.petitioner, + role: ROLES.petitioner, }, }, }); @@ -31,7 +34,7 @@ describe('updatePartyTypeAction', () => { value: 'Myself', }), ); - expect(state.form.partyType).toEqual(ContactFactory.PARTY_TYPES.petitioner); + expect(state.form.partyType).toEqual(PARTY_TYPES.petitioner); }); it('sets the partyType to "Petitioner & Deceased Spouse" when "isSpouseDeceased" is updated to "Yes"', async () => { @@ -42,9 +45,7 @@ describe('updatePartyTypeAction', () => { value: 'Yes', }), ); - expect(state.form.partyType).toEqual( - ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, - ); + expect(state.form.partyType).toEqual(PARTY_TYPES.petitionerDeceasedSpouse); }); it('sets the partyType to "Petitioner & Spouse" when "isSpouseDeceased" is updated to "No"', async () => { @@ -55,9 +56,7 @@ describe('updatePartyTypeAction', () => { value: 'No', }), ); - expect(state.form.partyType).toEqual( - ContactFactory.PARTY_TYPES.petitionerSpouse, - ); + expect(state.form.partyType).toEqual(PARTY_TYPES.petitionerSpouse); }); it('sets the partyType to "Donor" when "otherType" is updated to "Donor"', async () => { @@ -68,7 +67,7 @@ describe('updatePartyTypeAction', () => { value: 'Donor', }), ); - expect(state.form.partyType).toEqual(ContactFactory.PARTY_TYPES.donor); + expect(state.form.partyType).toEqual(PARTY_TYPES.donor); }); it('sets the partyType to "Transferee" when "otherType" is updated to "Transferee"', async () => { @@ -79,7 +78,7 @@ describe('updatePartyTypeAction', () => { value: 'Transferee', }), ); - expect(state.form.partyType).toEqual(ContactFactory.PARTY_TYPES.transferee); + expect(state.form.partyType).toEqual(PARTY_TYPES.transferee); }); it('sets the partyType to "Surviving Spouse" when "otherType" is updated to "Deceased Spouse"', async () => { @@ -90,9 +89,7 @@ describe('updatePartyTypeAction', () => { value: 'Deceased Spouse', }), ); - expect(state.form.partyType).toEqual( - ContactFactory.PARTY_TYPES.survivingSpouse, - ); + expect(state.form.partyType).toEqual(PARTY_TYPES.survivingSpouse); }); it('sets the partyType to the props.value passed in when key is "businessType"', async () => { @@ -158,7 +155,7 @@ describe('updatePartyTypeAction', () => { }, { constants: { - COUNTRY_TYPES: ContactFactory.COUNTRY_TYPES, + COUNTRY_TYPES: COUNTRY_TYPES, PARTY_TYPES: [], }, form: { @@ -183,7 +180,7 @@ describe('updatePartyTypeAction', () => { }, { constants: { - COUNTRY_TYPES: ContactFactory.COUNTRY_TYPES, + COUNTRY_TYPES: COUNTRY_TYPES, PARTY_TYPES: [], }, form: { @@ -208,7 +205,7 @@ describe('updatePartyTypeAction', () => { }, { constants: { - COUNTRY_TYPES: ContactFactory.COUNTRY_TYPES, + COUNTRY_TYPES: COUNTRY_TYPES, PARTY_TYPES: [], }, form: { diff --git a/web-client/src/presenter/actions/StartCaseInternal/computeStatisticDatesAction.js b/web-client/src/presenter/actions/StartCaseInternal/computeStatisticDatesAction.js index 7b479f31e67..8a6e7878e3a 100644 --- a/web-client/src/presenter/actions/StartCaseInternal/computeStatisticDatesAction.js +++ b/web-client/src/presenter/actions/StartCaseInternal/computeStatisticDatesAction.js @@ -1,5 +1,31 @@ import { state } from 'cerebral'; +export const combineLastDateOfPeriodFields = ({ applicationContext, form }) => { + const newForm = { + ...form, + }; + + if ( + applicationContext + .getUtilities() + .isValidDateString( + `${newForm.lastDateOfPeriodMonth}-${newForm.lastDateOfPeriodDay}-${newForm.lastDateOfPeriodYear}`, + ) + ) { + const computedLastDateOfPeriod = applicationContext + .getUtilities() + .createISODateStringFromObject({ + day: newForm.lastDateOfPeriodDay, + month: newForm.lastDateOfPeriodMonth, + year: newForm.lastDateOfPeriodYear, + }); + newForm.lastDateOfPeriod = computedLastDateOfPeriod; + } else { + newForm.lastDateOfPeriod = undefined; + } + return newForm; +}; + /** * computes the dates for the statistics array from the form * @@ -15,27 +41,12 @@ export const computeStatisticDatesAction = ({ }) => { let statistics = get(state.form.statistics) || []; - statistics = statistics.map(statistic => { - if ( - applicationContext - .getUtilities() - .isValidDateString( - `${statistic.lastDateOfPeriodMonth}-${statistic.lastDateOfPeriodDay}-${statistic.lastDateOfPeriodYear}`, - ) - ) { - const computedLastDateOfPeriod = applicationContext - .getUtilities() - .createISODateStringFromObject({ - day: statistic.lastDateOfPeriodDay, - month: statistic.lastDateOfPeriodMonth, - year: statistic.lastDateOfPeriodYear, - }); - statistic.lastDateOfPeriod = computedLastDateOfPeriod; - } else { - statistic.lastDateOfPeriod = undefined; - } - return statistic; - }); + statistics = statistics.map(statistic => + combineLastDateOfPeriodFields({ + applicationContext, + form: statistic, + }), + ); store.set(state.form.statistics, statistics); }; diff --git a/web-client/src/presenter/actions/StartCaseInternal/filterEmptyStatisticsAction.js b/web-client/src/presenter/actions/StartCaseInternal/filterEmptyStatisticsAction.js index 4bab2dd6e03..f4067b7331b 100644 --- a/web-client/src/presenter/actions/StartCaseInternal/filterEmptyStatisticsAction.js +++ b/web-client/src/presenter/actions/StartCaseInternal/filterEmptyStatisticsAction.js @@ -22,8 +22,8 @@ export const filterEmptyStatisticsAction = ({ statistic.lastDateOfPeriodDay || statistic.lastDateOfPeriodMonth || statistic.lastDateOfPeriodYear || - statistic.deficiencyAmount || - statistic.totalPenalties, + statistic.irsDeficiencyAmount || + statistic.irsTotalPenalties, ); const { CASE_TYPES_MAP } = applicationContext.getConstants(); diff --git a/web-client/src/presenter/actions/StartCaseInternal/getInitialNextStepAction.js b/web-client/src/presenter/actions/StartCaseInternal/getInitialNextStepAction.js deleted file mode 100644 index ad7e365c4d4..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/getInitialNextStepAction.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * get next step prop as set to the first step - * - * @returns {object} the prop of the alert success message - */ -export const getInitialNextStepAction = () => { - return { - nextStep: 1, - }; -}; diff --git a/web-client/src/presenter/actions/StartCaseInternal/getInitialNextStepAction.test.js b/web-client/src/presenter/actions/StartCaseInternal/getInitialNextStepAction.test.js deleted file mode 100644 index e411c61d92d..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/getInitialNextStepAction.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import { getInitialNextStepAction } from './getInitialNextStepAction'; -import { runAction } from 'cerebral/test'; - -describe('getInitialNextStepAction', () => { - it('should return nextStep prop', async () => { - const result = await runAction(getInitialNextStepAction, {}); - expect(result.output.nextStep).toBeTruthy(); - }); -}); diff --git a/web-client/src/presenter/actions/StartCaseInternal/getSaveCaseForLaterAlertSuccessAction.js b/web-client/src/presenter/actions/StartCaseInternal/getSaveCaseForLaterAlertSuccessAction.js deleted file mode 100644 index 0f33a45c80d..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/getSaveCaseForLaterAlertSuccessAction.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * get alert message when a new paper petition is saved for later - * - * @returns {object} the prop of the alert success message - */ -export const getSaveCaseForLaterAlertSuccessAction = () => { - return { - alertSuccess: { - message: 'Petition saved for later service.', - }, - }; -}; diff --git a/web-client/src/presenter/actions/StartCaseInternal/getSaveCaseForLaterAlertSuccessAction.test.js b/web-client/src/presenter/actions/StartCaseInternal/getSaveCaseForLaterAlertSuccessAction.test.js deleted file mode 100644 index f0abd2a7347..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/getSaveCaseForLaterAlertSuccessAction.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import { getSaveCaseForLaterAlertSuccessAction } from './getSaveCaseForLaterAlertSuccessAction'; -import { runAction } from 'cerebral/test'; - -describe('getSaveCaseForLaterAlertSuccessAction', () => { - it('should return alertSuccess prop', async () => { - const result = await runAction(getSaveCaseForLaterAlertSuccessAction, {}); - expect(result.output.alertSuccess).toBeTruthy(); - }); -}); diff --git a/web-client/src/presenter/actions/StartCaseInternal/setFormContactSecondaryAction.js b/web-client/src/presenter/actions/StartCaseInternal/setFormContactSecondaryAction.js deleted file mode 100644 index 7fa220b041c..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/setFormContactSecondaryAction.js +++ /dev/null @@ -1,15 +0,0 @@ -import { state } from 'cerebral'; - -/** - * sets contactSecondary with contact prop - * - * @param {object} providers the providers object - * @param {object} providers.props the cerebral props - * @param {object} providers.store the cerebral store - * @returns {void} - */ -export const setFormContactSecondaryAction = ({ props, store }) => { - const { contact } = props; - - store.set(state.form.contactSecondary, contact); -}; diff --git a/web-client/src/presenter/actions/StartCaseInternal/setFormContactSecondaryAction.test.js b/web-client/src/presenter/actions/StartCaseInternal/setFormContactSecondaryAction.test.js deleted file mode 100644 index cbb56dfae95..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/setFormContactSecondaryAction.test.js +++ /dev/null @@ -1,20 +0,0 @@ -import { runAction } from 'cerebral/test'; -import { setFormContactSecondaryAction } from './setFormContactSecondaryAction'; - -describe('setFormContactSecondaryAction', () => { - it('sets form.contactSecondary to the contact prop', async () => { - const result = await runAction(setFormContactSecondaryAction, { - props: { - contact: { - city: 'Flavortown', - name: 'Guy Fieri', - }, - }, - }); - - expect(result.state.form.contactSecondary).toMatchObject({ - city: 'Flavortown', - name: 'Guy Fieri', - }); - }); -}); diff --git a/web-client/src/presenter/actions/StartCaseInternal/setStartInternalCaseTabAction.js b/web-client/src/presenter/actions/StartCaseInternal/setStartInternalCaseTabAction.js deleted file mode 100644 index 97ed76efea7..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/setStartInternalCaseTabAction.js +++ /dev/null @@ -1,12 +0,0 @@ -import { state } from 'cerebral'; - -/** - * sets currentViewMetadata.startCaseInternal tab to props tab - * - * @param {object} providers the providers object - * @param {Function} providers.props the cerebral props object - * @param {Function} providers.store the cerebral store object - */ -export const setStartInternalCaseTabAction = ({ props, store }) => { - store.set(state.currentViewMetadata.startCaseInternal.tab, props.tab); -}; diff --git a/web-client/src/presenter/actions/StartCaseInternal/setStartInternalCaseTabAction.test.js b/web-client/src/presenter/actions/StartCaseInternal/setStartInternalCaseTabAction.test.js deleted file mode 100644 index 1aeb6ea625b..00000000000 --- a/web-client/src/presenter/actions/StartCaseInternal/setStartInternalCaseTabAction.test.js +++ /dev/null @@ -1,14 +0,0 @@ -import { runAction } from 'cerebral/test'; -import { setStartInternalCaseTabAction } from './setStartInternalCaseTabAction'; - -describe('setStartInternalCaseTabAction', () => { - it('should set currentViewMetadata.startCaseInternal tab from props', async () => { - const result = await runAction(setStartInternalCaseTabAction, { - props: { tab: 'caseInfo' }, - }); - - expect(result.state.currentViewMetadata.startCaseInternal.tab).toEqual( - 'caseInfo', - ); - }); -}); diff --git a/web-client/src/presenter/actions/StartCaseInternal/updateOrderForFilingFeeAction.test.js b/web-client/src/presenter/actions/StartCaseInternal/updateOrderForFilingFeeAction.test.js index 6e50e0312d7..d8058fb7a9e 100644 --- a/web-client/src/presenter/actions/StartCaseInternal/updateOrderForFilingFeeAction.test.js +++ b/web-client/src/presenter/actions/StartCaseInternal/updateOrderForFilingFeeAction.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../../../../shared/src/business/entities/cases/Case'; +import { PAYMENT_STATUS } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -16,7 +16,7 @@ describe('updateOrderForFilingFeeAction', () => { }, props: { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.UNPAID, + value: PAYMENT_STATUS.UNPAID, }, state: { form: {}, @@ -33,7 +33,7 @@ describe('updateOrderForFilingFeeAction', () => { }, props: { key: 'anotherField', - value: Case.PAYMENT_STATUS.UNPAID, + value: PAYMENT_STATUS.UNPAID, }, state: { form: { @@ -52,7 +52,7 @@ describe('updateOrderForFilingFeeAction', () => { }, props: { key: 'petitionPaymentStatus', - value: Case.PAYMENT_STATUS.PAID, + value: PAYMENT_STATUS.PAID, }, state: { form: {}, diff --git a/web-client/src/presenter/actions/StartCaseInternal/updateOrderForOdsAction.test.js b/web-client/src/presenter/actions/StartCaseInternal/updateOrderForOdsAction.test.js index e4840bb8a61..fecf958f9e5 100644 --- a/web-client/src/presenter/actions/StartCaseInternal/updateOrderForOdsAction.test.js +++ b/web-client/src/presenter/actions/StartCaseInternal/updateOrderForOdsAction.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../../../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -17,7 +17,7 @@ describe('updateOrderForOdsAction', () => { props: { key: 'partyType' }, state: { form: { - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); @@ -33,7 +33,7 @@ describe('updateOrderForOdsAction', () => { props: { key: 'partyType' }, state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -50,7 +50,7 @@ describe('updateOrderForOdsAction', () => { state: { form: { ownershipDisclosureFile: 'the file!', - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); @@ -67,7 +67,7 @@ describe('updateOrderForOdsAction', () => { state: { form: { orderForOds: false, - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); diff --git a/web-client/src/presenter/actions/TrialSession/getAddCaseToTrialSessionCalendarAlertWarningAction.js b/web-client/src/presenter/actions/TrialSession/getAddCaseToTrialSessionCalendarAlertWarningAction.js deleted file mode 100644 index 304b0232785..00000000000 --- a/web-client/src/presenter/actions/TrialSession/getAddCaseToTrialSessionCalendarAlertWarningAction.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * set warning message when the case added to a trial session calendar is created with a paper case - * - * @param {object} providers the providers object - * @param {object} providers.props the props object - * @returns {object} the prop of the alert success message - */ -export const getAddCaseToTrialSessionCalendarAlertWarningAction = ({ - props, -}) => { - const { docketNumber, docketNumberSuffix } = props.caseDetail; - - const displayDocketNumber = `${docketNumber}${ - docketNumberSuffix ? docketNumberSuffix : '' - }`; - - return { - alertWarning: { - message: `Print and mail all paper service documents for ${displayDocketNumber} now.`, - }, - }; -}; diff --git a/web-client/src/presenter/actions/TrialSession/getAddCaseToTrialSessionCalendarAlertWarningAction.test.js b/web-client/src/presenter/actions/TrialSession/getAddCaseToTrialSessionCalendarAlertWarningAction.test.js deleted file mode 100644 index d934d373e4a..00000000000 --- a/web-client/src/presenter/actions/TrialSession/getAddCaseToTrialSessionCalendarAlertWarningAction.test.js +++ /dev/null @@ -1,24 +0,0 @@ -import { getAddCaseToTrialSessionCalendarAlertWarningAction } from './getAddCaseToTrialSessionCalendarAlertWarningAction'; -import { runAction } from 'cerebral/test'; - -describe('getAddCaseToTrialSessionCalendarAlertWarningAction', () => { - it('should set state.alertWarning with the print paper service for parties message', async () => { - const result = await runAction( - getAddCaseToTrialSessionCalendarAlertWarningAction, - { - props: { - caseDetail: { - docketNumber: '101-19', - docketNumberSuffix: 'P', - }, - }, - }, - ); - - expect(result.output).toEqual({ - alertWarning: { - message: 'Print and mail all paper service documents for 101-19P now.', - }, - }); - }); -}); diff --git a/web-client/src/presenter/actions/TrialSessionWorkingCopy/unsetUserCaseNoteFromTrialSessionWorkingCopyAction.js b/web-client/src/presenter/actions/TrialSessionWorkingCopy/unsetUserCaseNoteFromTrialSessionWorkingCopyAction.js deleted file mode 100644 index 9ef7e42370a..00000000000 --- a/web-client/src/presenter/actions/TrialSessionWorkingCopy/unsetUserCaseNoteFromTrialSessionWorkingCopyAction.js +++ /dev/null @@ -1,22 +0,0 @@ -import { state } from 'cerebral'; -import { unset } from 'lodash'; - -/** - * update props from modal state to pass to other actions - * - * @param {object} providers the providers object - * @param {object} providers.get the cerebral get function - * @param {object} providers.props the cerebral props object - * @param {object} providers.store the cerebral store - */ -export const unsetUserCaseNoteFromTrialSessionWorkingCopyAction = ({ - get, - props, - store, -}) => { - const workingCopy = get(state.trialSessionWorkingCopy); - - unset(workingCopy, ['caseMetadata', props.docketNumber, 'notes']); - - store.set(state.trialSessionWorkingCopy, workingCopy); -}; diff --git a/web-client/src/presenter/actions/TrialSessionWorkingCopy/unsetUserCaseNoteFromTrialSessionWorkingCopyAction.test.js b/web-client/src/presenter/actions/TrialSessionWorkingCopy/unsetUserCaseNoteFromTrialSessionWorkingCopyAction.test.js deleted file mode 100644 index 10e75d5f394..00000000000 --- a/web-client/src/presenter/actions/TrialSessionWorkingCopy/unsetUserCaseNoteFromTrialSessionWorkingCopyAction.test.js +++ /dev/null @@ -1,32 +0,0 @@ -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { unsetUserCaseNoteFromTrialSessionWorkingCopyAction } from './unsetUserCaseNoteFromTrialSessionWorkingCopyAction'; - -describe('unsetUserCaseNoteFromTrialSessionWorkingCopyAction', () => { - it('should set the modal caseId state', async () => { - const result = await runAction( - unsetUserCaseNoteFromTrialSessionWorkingCopyAction, - { - modules: { - presenter, - }, - props: { - docketNumber: '123', - }, - state: { - trialSessionWorkingCopy: { - caseMetadata: { - '123': { - notes: 'here are some notes', - }, - }, - }, - }, - }, - ); - - expect(result.state.trialSessionWorkingCopy.caseMetadata['123']).toEqual( - {}, - ); - }); -}); diff --git a/web-client/src/presenter/actions/TrialSessionWorkingCopy/updateUserCaseNoteInTrialSessionWorkingCopyAction.js b/web-client/src/presenter/actions/TrialSessionWorkingCopy/updateUserCaseNoteInTrialSessionWorkingCopyAction.js deleted file mode 100644 index 19b5b0d8e4c..00000000000 --- a/web-client/src/presenter/actions/TrialSessionWorkingCopy/updateUserCaseNoteInTrialSessionWorkingCopyAction.js +++ /dev/null @@ -1,22 +0,0 @@ -import { set } from 'lodash'; -import { state } from 'cerebral'; - -/** - * update props from modal state to pass to other actions - * - * @param {object} providers the providers object - * @param {object} providers.get the cerebral get function - * @param {object} providers.props the cerebral props object - * @param {object} providers.store the cerebral store - */ -export const updateUserCaseNoteInTrialSessionWorkingCopyAction = ({ - get, - props, - store, -}) => { - const workingCopy = get(state.trialSessionWorkingCopy); - - set(workingCopy, ['caseMetadata', props.docketNumber, 'notes'], props.notes); - - store.set(state.trialSessionWorkingCopy, workingCopy); -}; diff --git a/web-client/src/presenter/actions/TrialSessionWorkingCopy/updateUserCaseNoteInTrialSessionWorkingCopyAction.test.js b/web-client/src/presenter/actions/TrialSessionWorkingCopy/updateUserCaseNoteInTrialSessionWorkingCopyAction.test.js deleted file mode 100644 index f03f602438a..00000000000 --- a/web-client/src/presenter/actions/TrialSessionWorkingCopy/updateUserCaseNoteInTrialSessionWorkingCopyAction.test.js +++ /dev/null @@ -1,33 +0,0 @@ -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { updateUserCaseNoteInTrialSessionWorkingCopyAction } from './updateUserCaseNoteInTrialSessionWorkingCopyAction'; - -describe('updateUserCaseNoteInTrialSessionWorkingCopyAction', () => { - it('should set the modal caseId state', async () => { - const result = await runAction( - updateUserCaseNoteInTrialSessionWorkingCopyAction, - { - modules: { - presenter, - }, - props: { - docketNumber: '123', - notes: 'we are a family', - }, - state: { - trialSessionWorkingCopy: { - caseMetadata: { - '123': { - notes: 'here are some notes', - }, - }, - }, - }, - }, - ); - - expect( - result.state.trialSessionWorkingCopy.caseMetadata['123'].notes, - ).toEqual('we are a family'); - }); -}); diff --git a/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.js b/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.js index 8d3f4b6ca44..89edc461847 100644 --- a/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.js +++ b/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.js @@ -8,5 +8,7 @@ import { state } from 'cerebral'; */ export const setCreateMessageModalDialogModalStateAction = ({ store }) => { store.set(state.modal.validationErrors, {}); - store.set(state.modal.form, {}); + store.set(state.modal.form, { + attachments: [], + }); }; diff --git a/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.test.js b/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.test.js index 28634cd9549..fa7c9c08ff6 100644 --- a/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.test.js +++ b/web-client/src/presenter/actions/WorkItem/setCreateMessageModalDialogModalStateAction.test.js @@ -16,7 +16,9 @@ describe('setCreateMessageModalDialogModalStateAction', () => { ); expect(result.state.modal).toEqual({ - form: {}, + form: { + attachments: [], + }, validationErrors: {}, }); }); diff --git a/web-client/src/presenter/actions/archiveDraftDocumentAction.js b/web-client/src/presenter/actions/archiveDraftDocumentAction.js index 2bae1727d5b..143d7904ab1 100644 --- a/web-client/src/presenter/actions/archiveDraftDocumentAction.js +++ b/web-client/src/presenter/actions/archiveDraftDocumentAction.js @@ -12,9 +12,9 @@ export const archiveDraftDocumentAction = async ({ get, store, }) => { - const { caseId, documentId, redirectToCaseDetail } = get( - state.archiveDraftDocument, - ); + const { documentId, redirectToCaseDetail } = get(state.archiveDraftDocument); + const caseId = get(state.caseDetail.caseId); + await applicationContext .getUseCases() .archiveDraftDocumentInteractor({ applicationContext, caseId, documentId }); diff --git a/web-client/src/presenter/actions/archiveDraftDocumentAction.test.js b/web-client/src/presenter/actions/archiveDraftDocumentAction.test.js index 7b5eb363a3c..7a1d9745330 100644 --- a/web-client/src/presenter/actions/archiveDraftDocumentAction.test.js +++ b/web-client/src/presenter/actions/archiveDraftDocumentAction.test.js @@ -16,10 +16,12 @@ describe('archiveDraftDocumentAction', () => { props: {}, state: { archiveDraftDocument: { - caseId: 'abc-123ghadsf-zdasdf', documentId: 'def-gfed213-441-abce-312f', documentTitle: 'document-title-123', }, + caseDetail: { + caseId: 'abc-123ghadsf-zdasdf', + }, }, }); @@ -39,11 +41,13 @@ describe('archiveDraftDocumentAction', () => { props: {}, state: { archiveDraftDocument: { - caseId: 'abc-123ghadsf-zdasdf', documentId: 'def-gfed213-441-abce-312f', documentTitle: 'document-title-123', redirectToCaseDetail: true, }, + caseDetail: { + caseId: 'abc-123ghadsf-zdasdf', + }, }, }); diff --git a/web-client/src/presenter/actions/caseAssociation/validateAddIrsPractitionerAction.test.js b/web-client/src/presenter/actions/caseAssociation/validateAddIrsPractitionerAction.test.js index 895d17b47c6..6e539b30cb5 100644 --- a/web-client/src/presenter/actions/caseAssociation/validateAddIrsPractitionerAction.test.js +++ b/web-client/src/presenter/actions/caseAssociation/validateAddIrsPractitionerAction.test.js @@ -13,7 +13,7 @@ describe('validateAddIrsPractitioner', () => { errorStub = jest.fn(); mockAddIrsPractitioner = { - user: { userId: 'abc' }, + user: { userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291' }, }; presenter.providers.applicationContext = applicationContextForClient; diff --git a/web-client/src/presenter/actions/caseAssociation/validateAddPrivatePractitionerAction.test.js b/web-client/src/presenter/actions/caseAssociation/validateAddPrivatePractitionerAction.test.js index a13eccdcd87..0859a61d6d3 100644 --- a/web-client/src/presenter/actions/caseAssociation/validateAddPrivatePractitionerAction.test.js +++ b/web-client/src/presenter/actions/caseAssociation/validateAddPrivatePractitionerAction.test.js @@ -15,7 +15,7 @@ describe('validateAddPrivatePractitioner', () => { mockAddPrivatePractitioner = { representingPrimary: true, - user: { userId: 'abc' }, + user: { userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291' }, }; presenter.providers.applicationContext = applicationContextForClient; diff --git a/web-client/src/presenter/actions/caseAssociation/validateEditIrsPractitionersAction.test.js b/web-client/src/presenter/actions/caseAssociation/validateEditIrsPractitionersAction.test.js index 9870efdcb20..f04d36d80c7 100644 --- a/web-client/src/presenter/actions/caseAssociation/validateEditIrsPractitionersAction.test.js +++ b/web-client/src/presenter/actions/caseAssociation/validateEditIrsPractitionersAction.test.js @@ -1,4 +1,4 @@ -import { SERVICE_INDICATOR_TYPES } from '../../../../../shared/src/business/entities/cases/CaseConstants'; +import { SERVICE_INDICATOR_TYPES } from '../../../../../shared/src/business/entities/EntityConstants'; import { presenter } from '../../presenter-mock'; import { runAction } from 'cerebral/test'; import { validateEditIrsPractitionersAction } from './validateEditIrsPractitionersAction'; diff --git a/web-client/src/presenter/actions/caseConsolidation/getConsolidatedCasesByUserAction.js b/web-client/src/presenter/actions/caseConsolidation/getConsolidatedCasesByUserAction.js index aeaf7ea6009..90425914bb4 100644 --- a/web-client/src/presenter/actions/caseConsolidation/getConsolidatedCasesByUserAction.js +++ b/web-client/src/presenter/actions/caseConsolidation/getConsolidatedCasesByUserAction.js @@ -1,22 +1,33 @@ +import { CASE_STATUS_TYPES } from '../../../../../shared/src/business/entities/EntityConstants'; import { orderBy } from 'lodash'; +import { state } from 'cerebral'; /** * Fetches the cases (including consolidated) associated with the petitioner who created them or the respondent who is associated with them. * * @param {object} providers the providers object * @param {object} providers.applicationContext needed for getting the getCasesByUser use case + * @param {object} providers.status the status determining the type of cases to be retrieved * @returns {object} contains the caseList returned from the getCasesByUser use case */ export const getConsolidatedCasesByUserAction = async ({ applicationContext, + get, }) => { - const { userId } = applicationContext.getCurrentUser(); - let caseList = await applicationContext - .getUseCases() - .getConsolidatedCasesByUserInteractor({ + const status = get(state.currentViewMetadata.caseList.tab); + + let caseList; + if (status !== CASE_STATUS_TYPES.closed) { + caseList = await applicationContext + .getUseCases() + .getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + } else { + caseList = await applicationContext.getUseCases().getClosedCasesInteractor({ applicationContext, - userId, }); + } caseList = orderBy(caseList, 'createdAt', 'desc'); return { caseList }; }; diff --git a/web-client/src/presenter/actions/caseConsolidation/getConsolidatedCasesByUserAction.test.js b/web-client/src/presenter/actions/caseConsolidation/getConsolidatedCasesByUserAction.test.js deleted file mode 100644 index a5d0c505643..00000000000 --- a/web-client/src/presenter/actions/caseConsolidation/getConsolidatedCasesByUserAction.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; -import { getConsolidatedCasesByUserAction } from './getConsolidatedCasesByUserAction'; -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('getConsolidatedCasesByUserAction', () => { - beforeAll(() => { - applicationContext.getUseCases().getConsolidatedCasesByUserInteractor = jest - .fn() - .mockReturnValue([ - { - caseId: 'case-id-234', - createdAt: '2019-07-20T20:20:15.680Z', - }, - { - caseId: 'case-id-123', - createdAt: '2019-07-19T20:20:15.680Z', - }, - { - caseId: 'case-id-345', - createdAt: '2019-07-21T20:20:15.680Z', - }, - ]); - applicationContext.getCurrentUser.mockReturnValue({ - userId: 'abc-123', - }); - - presenter.providers.applicationContext = applicationContext; - }); - - it('gets the consolidated cases by userId', async () => { - const { output } = await runAction(getConsolidatedCasesByUserAction, { - modules: { presenter }, - }); - - expect(output).toMatchObject({ - caseList: [ - { caseId: 'case-id-345', createdAt: '2019-07-21T20:20:15.680Z' }, - { caseId: 'case-id-234', createdAt: '2019-07-20T20:20:15.680Z' }, - { caseId: 'case-id-123', createdAt: '2019-07-19T20:20:15.680Z' }, - ], - }); - }); -}); diff --git a/web-client/src/presenter/actions/caseConsolidation/getOpenAndClosedCasesByUserAction.js b/web-client/src/presenter/actions/caseConsolidation/getOpenAndClosedCasesByUserAction.js new file mode 100644 index 00000000000..8fb781b4228 --- /dev/null +++ b/web-client/src/presenter/actions/caseConsolidation/getOpenAndClosedCasesByUserAction.js @@ -0,0 +1,30 @@ +import { orderBy } from 'lodash'; + +/** + * Fetches the cases (including consolidated) associated with the petitioner who + * created them or the respondent who is associated with them. + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext needed for getting the getCasesByUser use case + * @returns {object} contains the caseList returned from the + * getOpenConsolidatedCasesInteractor and getClosedCasesInteractor use cases + */ +export const getOpenAndClosedCasesByUserAction = async ({ + applicationContext, +}) => { + let openCaseList = await applicationContext + .getUseCases() + .getOpenConsolidatedCasesInteractor({ + applicationContext, + }); + let closedCaseList = await applicationContext + .getUseCases() + .getClosedCasesInteractor({ + applicationContext, + }); + + openCaseList = orderBy(openCaseList, 'createdAt', 'desc'); + closedCaseList = orderBy(closedCaseList, 'createdAt', 'desc'); + + return { closedCaseList, openCaseList }; +}; diff --git a/web-client/src/presenter/actions/caseConsolidation/getOpenAndClosedCasesByUserAction.test.js b/web-client/src/presenter/actions/caseConsolidation/getOpenAndClosedCasesByUserAction.test.js new file mode 100644 index 00000000000..e3d3ee991a6 --- /dev/null +++ b/web-client/src/presenter/actions/caseConsolidation/getOpenAndClosedCasesByUserAction.test.js @@ -0,0 +1,70 @@ +import { applicationContextForClient as applicationContext } from '../../../../../shared/src/business/test/createTestApplicationContext'; +import { getOpenAndClosedCasesByUserAction } from './getOpenAndClosedCasesByUserAction'; +import { presenter } from '../../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('getOpenAndClosedCasesByUserAction', () => { + beforeAll(() => { + applicationContext + .getUseCases() + .getOpenConsolidatedCasesInteractor.mockReturnValue([ + { + caseId: 'case-id-234', + createdAt: '2019-07-20T20:20:15.680Z', + }, + { + caseId: 'case-id-123', + createdAt: '2019-07-19T20:20:15.680Z', + }, + { + caseId: 'case-id-345', + createdAt: '2019-07-21T20:20:15.680Z', + }, + ]); + applicationContext.getUseCases().getClosedCasesInteractor.mockReturnValue([ + { + caseId: 'case-id-234', + createdAt: '2019-07-20T20:20:15.680Z', + }, + { + caseId: 'case-id-123', + createdAt: '2019-07-19T20:20:15.680Z', + }, + { + caseId: 'case-id-345', + createdAt: '2019-07-21T20:20:15.680Z', + }, + ]); + applicationContext.getCurrentUser.mockReturnValue({ + userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291', + }); + + presenter.providers.applicationContext = applicationContext; + }); + + it('gets the consolidated cases by userId', async () => { + const { output } = await runAction(getOpenAndClosedCasesByUserAction, { + modules: { presenter }, + state: { + currentViewMetadata: { + caseList: { + tab: 'Open', + }, + }, + }, + }); + + expect(output).toMatchObject({ + closedCaseList: [ + { caseId: 'case-id-345', createdAt: '2019-07-21T20:20:15.680Z' }, + { caseId: 'case-id-234', createdAt: '2019-07-20T20:20:15.680Z' }, + { caseId: 'case-id-123', createdAt: '2019-07-19T20:20:15.680Z' }, + ], + openCaseList: [ + { caseId: 'case-id-345', createdAt: '2019-07-21T20:20:15.680Z' }, + { caseId: 'case-id-234', createdAt: '2019-07-20T20:20:15.680Z' }, + { caseId: 'case-id-123', createdAt: '2019-07-19T20:20:15.680Z' }, + ], + }); + }); +}); diff --git a/web-client/src/presenter/actions/caseExistsAndIsNotSealedAction.js b/web-client/src/presenter/actions/caseExistsAndIsNotSealedAction.js new file mode 100644 index 00000000000..732be5f29c7 --- /dev/null +++ b/web-client/src/presenter/actions/caseExistsAndIsNotSealedAction.js @@ -0,0 +1,27 @@ +/** + * Checks for existence of a case using the getCaseForPublicDocketSearchInteractor use case using the props.docketNumber + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext needed for getting the getCaseForPublicDocketSearchInteractor use case + * @param {object} providers.path provides execution path choices depending on the existence of the case + * @param {object} providers.props the cerebral props object containing props.docketNumber + * @returns {object} contains the caseDetail returned from the use case + */ +export const caseExistsAndIsNotSealedAction = async ({ + applicationContext, + path, + props, +}) => { + try { + const caseDetail = await applicationContext + .getUseCases() + .getCaseForPublicDocketSearchInteractor({ + applicationContext, + docketNumber: props.caseId, + }); + + return path.success({ caseDetail }); + } catch (e) { + return path.error(); + } +}; diff --git a/web-client/src/presenter/actions/caseExistsAndIsNotSealedAction.test.js b/web-client/src/presenter/actions/caseExistsAndIsNotSealedAction.test.js new file mode 100644 index 00000000000..55b6e8b2920 --- /dev/null +++ b/web-client/src/presenter/actions/caseExistsAndIsNotSealedAction.test.js @@ -0,0 +1,73 @@ +import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { caseExistsAndIsNotSealedAction } from './caseExistsAndIsNotSealedAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('caseExistsAndIsNotSealedAction', () => { + const successMock = jest.fn(); + const errorMock = jest.fn(); + + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + presenter.providers.path = { + error: errorMock, + success: successMock, + }; + }); + + it('calls the interactor for fetching the case', async () => { + await runAction(caseExistsAndIsNotSealedAction, { + modules: { + presenter, + }, + props: { + code: '123', + }, + state: {}, + }); + + expect( + applicationContext.getUseCases().getCaseForPublicDocketSearchInteractor + .mock.calls.length, + ).toEqual(1); + }); + + it('calls the success path when the interactor runs successfully and the case', async () => { + applicationContext + .getUseCases() + .getCaseForPublicDocketSearchInteractor.mockReturnValue(MOCK_CASE); + + await runAction(caseExistsAndIsNotSealedAction, { + modules: { + presenter, + }, + props: { + code: '123', + }, + state: {}, + }); + + expect(successMock).toHaveBeenCalled(); + }); + + it('calls the error path when an error is encountered', async () => { + applicationContext + .getUseCases() + .getCaseForPublicDocketSearchInteractor.mockImplementation(() => { + throw new Error('Nope!'); + }); + + await runAction(caseExistsAndIsNotSealedAction, { + modules: { + presenter, + }, + props: { + code: '123', + }, + state: {}, + }); + + expect(errorMock).toHaveBeenCalled(); + }); +}); diff --git a/web-client/src/presenter/actions/chooseMessageBoxAction.js b/web-client/src/presenter/actions/chooseMessageBoxAction.js new file mode 100644 index 00000000000..9f4afa6a295 --- /dev/null +++ b/web-client/src/presenter/actions/chooseMessageBoxAction.js @@ -0,0 +1,19 @@ +import { state } from 'cerebral'; + +/** + * Used for changing the message queue (my, section) and box (inbox, outbox, completed) from props + * + * @param {object} providers the providers object + * @param {object} providers.path the next object in the path (this is defined in the sequence right after this action is invoked) + * @param {object} providers.props the cerebral props object + * @param {object} providers.store the cerebral store object + * @returns {*} returns the next action in the sequence's path + */ +export const chooseMessageBoxAction = ({ path, props, store }) => { + store.set(state.messageBoxToDisplay.queue, props.queue); + store.set(state.messageBoxToDisplay.box, props.box); + + const messageBoxPath = `${props.queue}${props.box}`; + + return path[messageBoxPath](); +}; diff --git a/web-client/src/presenter/actions/chooseMessageBoxAction.test.js b/web-client/src/presenter/actions/chooseMessageBoxAction.test.js new file mode 100644 index 00000000000..cf344065763 --- /dev/null +++ b/web-client/src/presenter/actions/chooseMessageBoxAction.test.js @@ -0,0 +1,29 @@ +import { chooseMessageBoxAction } from './chooseMessageBoxAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('chooseMessageBoxAction', () => { + const myinboxMock = jest.fn(); + + beforeAll(() => { + presenter.providers.path = { + myinbox: myinboxMock, + }; + }); + + it('sets state.messageBoxToDisplay from props and calls the path to the correct box', async () => { + const { state } = await runAction(chooseMessageBoxAction, { + modules: { presenter }, + props: { + box: 'inbox', + queue: 'my', + }, + }); + + expect(state.messageBoxToDisplay).toEqual({ + box: 'inbox', + queue: 'my', + }); + expect(myinboxMock).toBeCalled(); + }); +}); diff --git a/web-client/src/presenter/actions/clearAddDeficiencyFormValuesAction.js b/web-client/src/presenter/actions/clearAddDeficiencyFormValuesAction.js new file mode 100644 index 00000000000..db4314858be --- /dev/null +++ b/web-client/src/presenter/actions/clearAddDeficiencyFormValuesAction.js @@ -0,0 +1,19 @@ +import { state } from 'cerebral'; + +/** + * clears the form values if the yearOrPeriod value changes + * + * @param {object} providers the providers object + * @param {Function} providers.get the cerebral get function + * @param {object} providers.props the cerebral props object + * @param {object} providers.store the cerebral store + */ +export const clearAddDeficiencyFormValuesAction = ({ get, props, store }) => { + if (props.key.includes('yearOrPeriod')) { + const statistic = get(state.form); + store.set(state.form, { + statisticId: statistic.statisticId, + yearOrPeriod: props.value, + }); + } +}; diff --git a/web-client/src/presenter/actions/clearAddDeficiencyFormValuesAction.test.js b/web-client/src/presenter/actions/clearAddDeficiencyFormValuesAction.test.js new file mode 100644 index 00000000000..dc5cf4fa662 --- /dev/null +++ b/web-client/src/presenter/actions/clearAddDeficiencyFormValuesAction.test.js @@ -0,0 +1,42 @@ +import { clearAddDeficiencyFormValuesAction } from './clearAddDeficiencyFormValuesAction'; +import { runAction } from 'cerebral/test'; + +describe('clearAddDeficiencyFormValuesAction', () => { + it('sets the form to a default state if yearOrPeriod is changed', async () => { + const result = await runAction(clearAddDeficiencyFormValuesAction, { + props: { + key: 'yearOrPeriod', + value: 'Year', + }, + state: { + form: { + statisticId: '810d832d-329e-4b78-92d0-2d4e0709d1d0', + yearOrPeriod: 'Period', + }, + }, + }); + + expect(result.state.form).toEqual({ + statisticId: '810d832d-329e-4b78-92d0-2d4e0709d1d0', + yearOrPeriod: 'Year', + }); + }); + + it('does nothing if the key of yearOrPeriod was not passed in', async () => { + const result = await runAction(clearAddDeficiencyFormValuesAction, { + props: { + key: 'amount', + value: 'yup', + }, + state: { + form: { + yearOrPeriod: 'Year', + }, + }, + }); + + expect(result.state.form).toEqual({ + yearOrPeriod: 'Year', + }); + }); +}); diff --git a/web-client/src/presenter/actions/clearStatisticsFormValuesAction.js b/web-client/src/presenter/actions/clearStatisticsFormValuesAction.js new file mode 100644 index 00000000000..c892942be22 --- /dev/null +++ b/web-client/src/presenter/actions/clearStatisticsFormValuesAction.js @@ -0,0 +1,20 @@ +import { state } from 'cerebral'; + +/** + * clears the statistics form values if the yearOrPeriod value changes + * + * @param {object} providers the providers object + * @param {object} providers.store the cerebral store + * @param {object} providers.props the cerebral props object + */ +export const clearStatisticsFormValuesAction = ({ props, store }) => { + if (props.key.includes('yearOrPeriod')) { + const index = props.key.split('.')[1]; + store.unset(state.form.statistics[index].lastDateOfPeriodDay); + store.unset(state.form.statistics[index].lastDateOfPeriodMonth); + store.unset(state.form.statistics[index].lastDateOfPeriodYear); + store.unset(state.form.statistics[index].year); + store.unset(state.form.statistics[index].irsDeficiencyAmount); + store.unset(state.form.statistics[index].irsTotalPenalties); + } +}; diff --git a/web-client/src/presenter/actions/clearStatisticsFormValuesAction.test.js b/web-client/src/presenter/actions/clearStatisticsFormValuesAction.test.js new file mode 100644 index 00000000000..2c9741f06d0 --- /dev/null +++ b/web-client/src/presenter/actions/clearStatisticsFormValuesAction.test.js @@ -0,0 +1,58 @@ +import { clearStatisticsFormValuesAction } from './clearStatisticsFormValuesAction'; +import { runAction } from 'cerebral/test'; + +describe('clearStatisticsFormValuesAction', () => { + it('should unset statistics form values if props.key contains yearOrPeriod', async () => { + const result = await runAction(clearStatisticsFormValuesAction, { + props: { + key: 'statistics.0.yearOrPeriod', + value: 'Year', + }, + state: { + form: { + statistics: [ + { + irsDeficiencyAmount: '123', + irsTotalPenalties: '123', + lastDateOfPeriodDay: '1', + lastDateOfPeriodMonth: '1', + lastDateOfPeriodYear: '2010', + year: '2012', + yearOrPeriod: 'Year', + }, + ], + }, + }, + }); + + expect(result.state.form).toEqual({ + statistics: [ + { + yearOrPeriod: 'Year', + }, + ], + }); + }); + + it('should not unset statistics form values if props.key does not contain yearOrPeriod', async () => { + const statisticsForm = { + irsDeficiencyAmount: '123', + irsTotalPenalties: '123', + lastDateOfPeriodDay: '1', + lastDateOfPeriodMonth: '1', + lastDateOfPeriodYear: '2010', + year: '2012', + }; + const result = await runAction(clearStatisticsFormValuesAction, { + props: { + key: 'statistics.0.year', + value: '2012', + }, + state: { + form: { statistics: [statisticsForm] }, + }, + }); + + expect(result.state.form).toEqual({ statistics: [statisticsForm] }); + }); +}); diff --git a/web-client/src/presenter/actions/completeDocumentSigningAction.js b/web-client/src/presenter/actions/completeDocumentSigningAction.js index ec8769ae041..2c277ceb982 100644 --- a/web-client/src/presenter/actions/completeDocumentSigningAction.js +++ b/web-client/src/presenter/actions/completeDocumentSigningAction.js @@ -37,7 +37,9 @@ export const completeDocumentSigningAction = async ({ const signedPdfBytes = await applicationContext .getUseCases() .generateSignedDocumentInteractor({ - pageIndex: pageNumber - 1, // pdf.js starts at 1 + applicationContext, + pageIndex: pageNumber - 1, + // pdf.js starts at 1 pdfData: await pdfjsObj.getData(), posX: x, posY: y, diff --git a/web-client/src/presenter/actions/completeDocumentSigningAction.test.js b/web-client/src/presenter/actions/completeDocumentSigningAction.test.js index e43f62fdca3..1de580dee4c 100644 --- a/web-client/src/presenter/actions/completeDocumentSigningAction.test.js +++ b/web-client/src/presenter/actions/completeDocumentSigningAction.test.js @@ -18,7 +18,7 @@ describe('completeDocumentSigningAction', () => { presenter.providers.applicationContext = applicationContext; applicationContext.getCurrentUser.mockReturnValue({ - userId: '1', + userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291', }); global.window = { diff --git a/web-client/src/presenter/actions/completeWorkItemAction.test.js b/web-client/src/presenter/actions/completeWorkItemAction.test.js index b14052a52ce..5fd6c537c7f 100644 --- a/web-client/src/presenter/actions/completeWorkItemAction.test.js +++ b/web-client/src/presenter/actions/completeWorkItemAction.test.js @@ -9,7 +9,7 @@ const { completeWorkItemInteractor } = applicationContext.getUseCases(); applicationContext.getCurrentUser.mockReturnValue({ name: 'Docket Clerk', - userId: 'docketclerk', + userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291', }); presenter.providers.path = { @@ -43,7 +43,7 @@ describe('completeWorkItemInteractor', () => { user: { name: 'Docket Clerk', token: 'docketclerk', - userId: 'docketclerk', + userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291', }, }, }); @@ -77,7 +77,7 @@ describe('completeWorkItemInteractor', () => { user: { name: 'Docket Clerk', token: 'docketclerk', - userId: 'docketclerk', + userId: '15adf875-8c3c-4e94-91e9-a4c1bff51291', }, }, }); diff --git a/web-client/src/presenter/actions/countryTypeFormContactChangeAction.js b/web-client/src/presenter/actions/countryTypeFormContactChangeAction.js deleted file mode 100644 index efd98ddaadb..00000000000 --- a/web-client/src/presenter/actions/countryTypeFormContactChangeAction.js +++ /dev/null @@ -1,24 +0,0 @@ -import { state } from 'cerebral'; - -/** - * unsets form contact info when countryType changes - * - * @param {object} providers the providers object - * @param {object} providers.store the cerebral store object - */ -export const countryTypeFormContactChangeAction = ({ store }) => { - [ - 'address1', - 'address2', - 'address3', - 'country', - 'postalCode', - 'phone', - 'state', - 'city', - ].forEach(field => { - store.unset(state.form.contact[field]); - }); - - store.set(state.validationErrors.contact, {}); -}; diff --git a/web-client/src/presenter/actions/countryTypeFormContactChangeAction.test.js b/web-client/src/presenter/actions/countryTypeFormContactChangeAction.test.js deleted file mode 100644 index ffece7003e5..00000000000 --- a/web-client/src/presenter/actions/countryTypeFormContactChangeAction.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import { countryTypeFormContactChangeAction } from './countryTypeFormContactChangeAction'; -import { runAction } from 'cerebral/test'; - -describe('countryTypeFormContactChangeAction', () => { - it('should clear contact info and validationErrors when changed', async () => { - const result = await runAction(countryTypeFormContactChangeAction, { - state: { - form: { - contact: { - address1: '123 Hello', - city: 'Howdy Town', - country: 'Argentina', - phone: '1234567890', - postalCode: '12345', - state: '', - }, - }, - validationErrors: { - contact: { - state: 'Invalid state', - }, - }, - }, - }); - - expect(result.state.validationErrors.contact).toEqual({}); - expect(result.state.form.contact.address1).toBeUndefined(); - expect(result.state.form.contact.city).toBeUndefined(); - expect(result.state.form.contact.country).toBeUndefined(); - expect(result.state.form.contact.phone).toBeUndefined(); - expect(result.state.form.contact.postalCode).toBeUndefined(); - expect(result.state.form.contact.state).toBeUndefined(); - }); -}); diff --git a/web-client/src/presenter/actions/createPractitionerUserAction.js b/web-client/src/presenter/actions/createPractitionerUserAction.js index 7103ad32501..27e85653f33 100644 --- a/web-client/src/presenter/actions/createPractitionerUserAction.js +++ b/web-client/src/presenter/actions/createPractitionerUserAction.js @@ -8,6 +8,7 @@ import { state } from 'cerebral'; * @param {Function} providers.get the cerebral get function * @param {object} providers.path the next object in the path * @param {object} providers.props the props passed in to the action + * @returns {object} path execution results */ export const createPractitionerUserAction = async ({ applicationContext, diff --git a/web-client/src/presenter/actions/defaultUpdateCaseModalValuesAction.test.js b/web-client/src/presenter/actions/defaultUpdateCaseModalValuesAction.test.js index 96258c32535..c796aa50d0d 100644 --- a/web-client/src/presenter/actions/defaultUpdateCaseModalValuesAction.test.js +++ b/web-client/src/presenter/actions/defaultUpdateCaseModalValuesAction.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { defaultUpdateCaseModalValuesAction } from './defaultUpdateCaseModalValuesAction'; import { runAction } from 'cerebral/test'; @@ -9,7 +9,7 @@ describe('defaultUpdateCaseModalValuesAction', () => { caseDetail: { associatedJudge: 'Chief Judge', caseCaption: 'A case caption', - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, modal: {}, }, @@ -18,7 +18,7 @@ describe('defaultUpdateCaseModalValuesAction', () => { expect(result.state.modal).toMatchObject({ associatedJudge: 'Chief Judge', caseCaption: 'A case caption', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, }); }); }); diff --git a/web-client/src/presenter/actions/deleteDeficiencyStatisticsAction.js b/web-client/src/presenter/actions/deleteDeficiencyStatisticsAction.js new file mode 100644 index 00000000000..0d10e0226da --- /dev/null +++ b/web-client/src/presenter/actions/deleteDeficiencyStatisticsAction.js @@ -0,0 +1,49 @@ +import { state } from 'cerebral'; + +/** + * deletes the statistic from the case + * + * @param {object} providers the providers object + * @param {object} providers.get the cerebral get function + * @param {object} providers.applicationContext the applicationContext + * @param {object} providers.path the next object in the path + * @returns {Promise<*>} the success or error path + */ +export const deleteDeficiencyStatisticsAction = async ({ + applicationContext, + get, + path, +}) => { + const { caseId } = get(state.caseDetail); + const { lastDateOfPeriod, statisticId, year, yearOrPeriod } = get(state.form); + + try { + await applicationContext.getUseCases().deleteDeficiencyStatisticInteractor({ + applicationContext, + caseId, + statisticId, + }); + + let successMessageDate; + + if (yearOrPeriod === 'Year') { + successMessageDate = year; + } else { + successMessageDate = applicationContext + .getUtilities() + .formatDateString(lastDateOfPeriod, 'MMDDYY'); + } + + return path.success({ + alertSuccess: { + message: `${successMessageDate} statistics deleted.`, + }, + }); + } catch (e) { + return path.error({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + } +}; diff --git a/web-client/src/presenter/actions/deleteDeficiencyStatisticsAction.test.js b/web-client/src/presenter/actions/deleteDeficiencyStatisticsAction.test.js new file mode 100644 index 00000000000..8b63bdf460a --- /dev/null +++ b/web-client/src/presenter/actions/deleteDeficiencyStatisticsAction.test.js @@ -0,0 +1,117 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { deleteDeficiencyStatisticsAction } from './deleteDeficiencyStatisticsAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +presenter.providers.applicationContext = applicationContext; +presenter.providers.path = { + error: jest.fn(), + success: jest.fn(), +}; + +describe('deleteDeficiencyStatisticsAction', () => { + it('calls the deficiency statistics delete interactor, returning the success path', async () => { + const statistic = { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriod: null, + statisticId: '3c4a440d-00c9-458d-a113-23cc833e09c5', + year: '2019', + yearOrPeriod: 'Year', + }; + + await runAction(deleteDeficiencyStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + statistics: [statistic], + }, + form: { ...statistic }, + }, + }); + + expect( + applicationContext.getUseCases().deleteDeficiencyStatisticInteractor, + ).toHaveBeenCalled(); + expect(presenter.providers.path.success).toHaveBeenCalledWith({ + alertSuccess: { message: '2019 statistics deleted.' }, + }); + }); + + it('calls the deficiency statistics delete interactor, returning the success path with a lastDateOfPeriod success message', async () => { + const statistic = { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriod: '2019-03-01T21:40:46.415Z', + statisticId: '3c4a440d-00c9-458d-a113-23cc833e09c5', + year: null, + yearOrPeriod: 'Period', + }; + + await runAction(deleteDeficiencyStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + statistics: [statistic], + }, + form: { ...statistic }, + }, + }); + + expect( + applicationContext.getUseCases().deleteDeficiencyStatisticInteractor, + ).toHaveBeenCalled(); + expect(presenter.providers.path.success).toHaveBeenCalledWith({ + alertSuccess: { message: '03/01/19 statistics deleted.' }, + }); + }); + + it('returns the error path if an error is encountered when calling the interactor', async () => { + presenter.providers.applicationContext + .getUseCases() + .deleteDeficiencyStatisticInteractor.mockImplementationOnce(() => { + throw new Error('error'); + }); + + const statistic = { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriod: null, + statisticId: '3c4a440d-00c9-458d-a113-23cc833e09c5', + year: '2019', + yearOrPeriod: 'Year', + }; + + await runAction(deleteDeficiencyStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + statistics: [statistic], + }, + form: { ...statistic }, + }, + }); + + expect(presenter.providers.path.success).not.toHaveBeenCalled(); + expect(presenter.providers.path.error).toHaveBeenCalledWith({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + }); +}); diff --git a/web-client/src/presenter/actions/deleteOtherStatisticsAction.js b/web-client/src/presenter/actions/deleteOtherStatisticsAction.js new file mode 100644 index 00000000000..9dc9ae7329b --- /dev/null +++ b/web-client/src/presenter/actions/deleteOtherStatisticsAction.js @@ -0,0 +1,37 @@ +import { state } from 'cerebral'; +/** + * clears the other statistics off the case + * + * @param {object} providers the providers object + * @param {object} providers.get the cerebral get function + * @param {object} providers.applicationContext the applicationContext + * @param {object} providers.path the next object in the path + * @returns {Promise<*>} the success or error path + */ +export const deleteOtherStatisticsAction = async ({ + applicationContext, + get, + path, +}) => { + const { caseId } = get(state.caseDetail); + + try { + await applicationContext.getUseCases().updateOtherStatisticsInteractor({ + applicationContext, + caseId, + damages: null, + litigationCosts: null, + }); + return path.success({ + alertSuccess: { + message: 'Other statistics deleted.', + }, + }); + } catch (e) { + return path.error({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + } +}; diff --git a/web-client/src/presenter/actions/deleteOtherStatisticsAction.test.js b/web-client/src/presenter/actions/deleteOtherStatisticsAction.test.js new file mode 100644 index 00000000000..91b968436af --- /dev/null +++ b/web-client/src/presenter/actions/deleteOtherStatisticsAction.test.js @@ -0,0 +1,54 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { deleteOtherStatisticsAction } from './deleteOtherStatisticsAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('deleteOtherStatisticsAction', () => { + beforeEach(() => { + presenter.providers.applicationContext = applicationContext; + presenter.providers.path = { + error: jest.fn(), + success: jest.fn(), + }; + }); + + it('should take the success path if no errors are found', async () => { + applicationContext + .getUseCases() + .updateOtherStatisticsInteractor.mockResolvedValue(null); + + await runAction(deleteOtherStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '101-19', + }, + }, + }); + + expect(presenter.providers.path.success).toBeCalled(); + }); + + it('should take the error path if no errors are found', async () => { + applicationContext + .getUseCases() + .updateOtherStatisticsInteractor.mockImplementation(() => { + throw new Error(); + }); + + await runAction(deleteOtherStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '101-19', + }, + }, + }); + + expect(presenter.providers.path.error).toBeCalled(); + }); +}); diff --git a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.js b/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.js index 45effc9648a..7998e9baab3 100644 --- a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.js +++ b/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.js @@ -11,7 +11,11 @@ import { state } from 'cerebral'; export const setDocumentToFormAction = ({ props, store }) => { const { caseDetail, documentId } = props; - const documentToSet = caseDetail.documents.find( + const allCaseDocuments = [ + ...(caseDetail.documents || []), + ...(caseDetail.correspondence || []), + ]; + const documentToSet = allCaseDocuments.find( document => document.documentId === documentId, ); diff --git a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.test.js b/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.test.js index 035c1709df9..2408dfd7998 100644 --- a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.test.js +++ b/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setDocumentToFormAction.test.js @@ -1,16 +1,19 @@ import { runAction } from 'cerebral/test'; import { setDocumentToFormAction } from './setDocumentToFormAction'; -const documentIdToEdit = '123'; -const documentToMatch = { - documentId: documentIdToEdit, - documentIdToEdit: documentIdToEdit, - documentType: 'Order', - primaryDocumentFile: true, -}; - describe('setDocumentToFormAction', () => { + let documentIdToEdit; + let documentToMatch; + it('sets state.form for the given case and documentId', async () => { + documentIdToEdit = '123'; + documentToMatch = { + documentId: documentIdToEdit, + documentIdToEdit: documentIdToEdit, + documentType: 'Order', + primaryDocumentFile: true, + }; + const result = await runAction(setDocumentToFormAction, { props: { caseDetail: { @@ -26,10 +29,49 @@ describe('setDocumentToFormAction', () => { documentId: documentIdToEdit, }, }); + expect(result.state.form).toEqual(documentToMatch); }); + it('sets state.form for the given case and documentId when the document is a correspondence', async () => { + documentIdToEdit = '234'; + const mockCorrespondence = { + documentId: '234', + documentTitle: 'a lovely correspondence', + }; + + const result = await runAction(setDocumentToFormAction, { + props: { + caseDetail: { + caseId: 'c123', + correspondence: [mockCorrespondence], + documents: [ + { + documentId: '321', + documentType: 'Petition', + }, + ], + }, + documentId: documentIdToEdit, + }, + }); + + expect(result.state.form).toEqual({ + ...mockCorrespondence, + documentIdToEdit: documentIdToEdit, + primaryDocumentFile: true, + }); + }); + it('does nothing if documentId does not match a document', async () => { + documentIdToEdit = '123'; + documentToMatch = { + documentId: documentIdToEdit, + documentIdToEdit: documentIdToEdit, + documentType: 'Order', + primaryDocumentFile: true, + }; + const result = await runAction(setDocumentToFormAction, { props: { caseDetail: { @@ -45,6 +87,7 @@ describe('setDocumentToFormAction', () => { documentId: '890', }, }); + expect(result.state.form).toBeUndefined(); }); }); diff --git a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setTabToInProgressAction.js b/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setTabToInProgressAction.js deleted file mode 100644 index 699edfad4d7..00000000000 --- a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setTabToInProgressAction.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * return the updated prop - * - * @returns {object} the new props - */ -export const setTabToInProgressAction = () => { - return { tab: 'inProgress' }; -}; diff --git a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setTabToInProgressAction.test.js b/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setTabToInProgressAction.test.js deleted file mode 100644 index e5e2ed5bea7..00000000000 --- a/web-client/src/presenter/actions/editUploadCourtIssuedDocument/setTabToInProgressAction.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { presenter } from '../../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { setTabToInProgressAction } from './setTabToInProgressAction'; - -describe('setTabToInProgressAction', () => { - it('should update the props', async () => { - const result = await runAction(setTabToInProgressAction, { - modules: { - presenter, - }, - }); - - expect(result.output).toEqual({ - tab: 'inProgress', - }); - }); -}); diff --git a/web-client/src/presenter/actions/expireSaveSuccessAction.js b/web-client/src/presenter/actions/expireSaveSuccessAction.js deleted file mode 100644 index e9fba4c10f9..00000000000 --- a/web-client/src/presenter/actions/expireSaveSuccessAction.js +++ /dev/null @@ -1,14 +0,0 @@ -import { state } from 'cerebral'; - -/** - * hides the green success message after clicking save on the edit petition page a short amount of time - * - * @param {object} providers the providers object - * @param {object} providers.store the cerebral store object used for setting state.screenMetadata.showSaveSuccess - */ -export const expireSaveSuccessAction = ({ store }) => { - const EXPIRE_TIMEOUT_MS = 2000; - setTimeout(() => { - store.set(state.screenMetadata.showSaveSuccess, false); - }, EXPIRE_TIMEOUT_MS); -}; diff --git a/web-client/src/presenter/actions/expireSaveSuccessAction.test.js b/web-client/src/presenter/actions/expireSaveSuccessAction.test.js deleted file mode 100644 index b3d9f5bd35a..00000000000 --- a/web-client/src/presenter/actions/expireSaveSuccessAction.test.js +++ /dev/null @@ -1,20 +0,0 @@ -import { expireSaveSuccessAction } from './expireSaveSuccessAction'; -import { runAction } from 'cerebral/test'; - -describe('expireSaveSuccessAction', () => { - it('should unset save success action after a few seconds', async () => { - const result = await runAction(expireSaveSuccessAction, { - state: { - screenMetadata: { - showSaveSuccess: true, - }, - }, - }); - // because code within action has a setTimeout, value remains the same - expect(result.state.screenMetadata.showSaveSuccess).toEqual(true); - - // wait 3 seconds - await new Promise(resolve => setTimeout(resolve, 3000)); - expect(result.state.screenMetadata.showSaveSuccess).toEqual(false); - }); -}); diff --git a/web-client/src/presenter/actions/generateDocketRecordPdfUrlAction.test.js b/web-client/src/presenter/actions/generateDocketRecordPdfUrlAction.test.js index 17ccd9ee1cd..5e5263d2162 100644 --- a/web-client/src/presenter/actions/generateDocketRecordPdfUrlAction.test.js +++ b/web-client/src/presenter/actions/generateDocketRecordPdfUrlAction.test.js @@ -10,7 +10,6 @@ describe('generateDocketRecordPdfUrlAction', () => { applicationContext .getUseCases() .generateDocketRecordPdfInteractor.mockResolvedValue(mockPdfUrl); - global.window = global; }); diff --git a/web-client/src/presenter/actions/generatePdfFromScanSessionAction.js b/web-client/src/presenter/actions/generatePdfFromScanSessionAction.js index 2b9f6efa0e8..87a9e8f5003 100644 --- a/web-client/src/presenter/actions/generatePdfFromScanSessionAction.js +++ b/web-client/src/presenter/actions/generatePdfFromScanSessionAction.js @@ -30,7 +30,7 @@ export const generatePdfFromScanSessionAction = async ({ // this blocks the browser const pdfBlob = await applicationContext .getUseCases() - .generatePDFFromJPGDataInteractor(scannedBuffer); + .generatePDFFromJPGDataInteractor(scannedBuffer, applicationContext); const file = new File([pdfBlob], 'myfile.pdf', { type: 'application/pdf', diff --git a/web-client/src/presenter/actions/getCaseAssociationAction.test.js b/web-client/src/presenter/actions/getCaseAssociationAction.test.js index 8ea952e7c36..df9bdeee83a 100644 --- a/web-client/src/presenter/actions/getCaseAssociationAction.test.js +++ b/web-client/src/presenter/actions/getCaseAssociationAction.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { getCaseAssociationAction } from './getCaseAssociationAction'; import { presenter } from '../presenter-mock'; @@ -13,7 +13,7 @@ describe('getCaseAssociation', () => { .verifyPendingCaseForUserInteractor.mockReturnValue(false); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }); }); @@ -41,7 +41,7 @@ describe('getCaseAssociation', () => { .getUseCases() .verifyPendingCaseForUserInteractor.mockReturnValue(true); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '1234', }); @@ -67,7 +67,7 @@ describe('getCaseAssociation', () => { .getUseCases() .verifyPendingCaseForUserInteractor.mockReturnValue(false); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '1234', }); @@ -93,7 +93,7 @@ describe('getCaseAssociation', () => { .getUseCases() .verifyPendingCaseForUserInteractor.mockReturnValue(false); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }); @@ -119,7 +119,7 @@ describe('getCaseAssociation', () => { .getUseCases() .verifyPendingCaseForUserInteractor.mockReturnValue(true); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }); @@ -145,7 +145,7 @@ describe('getCaseAssociation', () => { .getUseCases() .verifyPendingCaseForUserInteractor.mockReturnValue(false); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '123', }); const results = await runAction(getCaseAssociationAction, { @@ -170,7 +170,7 @@ describe('getCaseAssociation', () => { .getUseCases() .verifyPendingCaseForUserInteractor.mockReturnValue(true); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '789', }); @@ -196,7 +196,7 @@ describe('getCaseAssociation', () => { .getUseCases() .verifyPendingCaseForUserInteractor.mockReturnValue(false); applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }); @@ -219,7 +219,7 @@ describe('getCaseAssociation', () => { it('should return false for isAssociated and pendingAssociation if the user is an irsSuperuser and the petition document is not served', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: '123', }); @@ -242,7 +242,7 @@ describe('getCaseAssociation', () => { it('should return true for isAssociated and false for pendingAssociation if the user is an irsSuperuser and the petition document is served', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: '123', }); diff --git a/web-client/src/presenter/actions/getCasesByUserAction.js b/web-client/src/presenter/actions/getCasesByUserAction.js deleted file mode 100644 index 4a98fd1e905..00000000000 --- a/web-client/src/presenter/actions/getCasesByUserAction.js +++ /dev/null @@ -1,20 +0,0 @@ -import { orderBy } from 'lodash'; - -/** - * Fetches the cases associated with the petitioner who created them or the respondent who is associated with them. - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext needed for getting the getCasesByUser use case - * @returns {object} contains the caseList returned from the getCasesByUser use case - */ -export const getCasesByUserAction = async ({ applicationContext }) => { - const { userId } = applicationContext.getCurrentUser(); - let caseList = await applicationContext - .getUseCases() - .getCasesByUserInteractor({ - applicationContext, - userId, - }); - caseList = orderBy(caseList, 'createdAt', 'desc'); - return { caseList }; -}; diff --git a/web-client/src/presenter/actions/getCasesByUserAction.test.js b/web-client/src/presenter/actions/getCasesByUserAction.test.js deleted file mode 100644 index 7add7ddcca3..00000000000 --- a/web-client/src/presenter/actions/getCasesByUserAction.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; -import { User } from '../../../../shared/src/business/entities/User'; -import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; -import { getCasesByUserAction } from './getCasesByUserAction'; -import { presenter } from '../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('getCasesByUserAction', () => { - beforeAll(() => { - presenter.providers.applicationContext = applicationContext; - applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.privatePractitioner, - userId: '123', - }); - applicationContext - .getUseCases() - .getCasesByUserInteractor.mockReturnValue([MOCK_CASE]); - }); - - it('should return a list of cases associated with the specified user', async () => { - const results = await runAction(getCasesByUserAction, { - modules: { - presenter, - }, - }); - - expect(results.output.caseList).toEqual([MOCK_CASE]); - }); -}); diff --git a/web-client/src/presenter/actions/getCreateCaseAlertSuccessAction.js b/web-client/src/presenter/actions/getCreateCaseAlertSuccessAction.js deleted file mode 100644 index 71ee7dc6db7..00000000000 --- a/web-client/src/presenter/actions/getCreateCaseAlertSuccessAction.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * creates the default success alert object - * - * @returns {object} the alertSuccess object with default strings - */ -export const getCreateCaseAlertSuccessAction = () => { - return { - alertSuccess: { - message: - 'Petition filed. Your receipt will be available once your petition is processed.', - }, - }; -}; diff --git a/web-client/src/presenter/actions/getFormDocumentUrlForPreviewAction.test.js b/web-client/src/presenter/actions/getFormDocumentUrlForPreviewAction.test.js index d74bea62e4d..1e194d54a21 100644 --- a/web-client/src/presenter/actions/getFormDocumentUrlForPreviewAction.test.js +++ b/web-client/src/presenter/actions/getFormDocumentUrlForPreviewAction.test.js @@ -1,11 +1,9 @@ -import { Document } from '../../../../shared/src/business/entities/Document'; +import { INITIAL_DOCUMENT_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { getFormDocumentUrlForPreviewAction } from './getFormDocumentUrlForPreviewAction'; import { presenter } from '../presenter-mock'; import { runAction } from 'cerebral/test'; -const { INITIAL_DOCUMENT_TYPES } = Document; - describe('getFormDocumentUrlForPreviewAction', () => { let form; diff --git a/web-client/src/presenter/actions/getInboxCaseMessagesForSectionAction.js b/web-client/src/presenter/actions/getInboxCaseMessagesForSectionAction.js new file mode 100644 index 00000000000..2d240120b60 --- /dev/null +++ b/web-client/src/presenter/actions/getInboxCaseMessagesForSectionAction.js @@ -0,0 +1,18 @@ +/** + * fetches the inbox case messages for the section + * + * @param {object} applicationContext object that contains all the context specific methods + * @returns {Promise<{CaseMessage: Array}>} a list of messages + */ +export const getInboxCaseMessagesForSectionAction = async ({ + applicationContext, +}) => { + const messages = await applicationContext + .getUseCases() + .getInboxCaseMessagesForSectionInteractor({ + applicationContext, + section: applicationContext.getCurrentUser().section, + }); + + return { messages }; +}; diff --git a/web-client/src/presenter/actions/getInboxCaseMessagesForSectionAction.test.js b/web-client/src/presenter/actions/getInboxCaseMessagesForSectionAction.test.js new file mode 100644 index 00000000000..8c729e46f6e --- /dev/null +++ b/web-client/src/presenter/actions/getInboxCaseMessagesForSectionAction.test.js @@ -0,0 +1,27 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { getInboxCaseMessagesForSectionAction } from './getInboxCaseMessagesForSectionAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('getInboxCaseMessagesForSectionAction', () => { + const message = { + messageId: '180bfc0c-4e8e-448a-802a-8fe027be85ef', + }; + + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + applicationContext + .getUseCases() + .getInboxCaseMessagesForSectionInteractor.mockReturnValue([message]); + }); + + it('returns the messages retrieved from the use case', async () => { + const results = await runAction(getInboxCaseMessagesForSectionAction, { + modules: { + presenter, + }, + state: {}, + }); + expect(results.output.messages).toEqual([message]); + }); +}); diff --git a/web-client/src/presenter/actions/getInboxCaseMessagesForUserAction.js b/web-client/src/presenter/actions/getInboxCaseMessagesForUserAction.js new file mode 100644 index 00000000000..00bf3404842 --- /dev/null +++ b/web-client/src/presenter/actions/getInboxCaseMessagesForUserAction.js @@ -0,0 +1,18 @@ +/** + * fetches the inbox case messages for the user + * + * @param {object} applicationContext object that contains all the context specific methods + * @returns {Promise<{CaseMessage: Array}>} a list of messages + */ +export const getInboxCaseMessagesForUserAction = async ({ + applicationContext, +}) => { + const messages = await applicationContext + .getUseCases() + .getInboxCaseMessagesForUserInteractor({ + applicationContext, + userId: applicationContext.getCurrentUser().userId, + }); + + return { messages }; +}; diff --git a/web-client/src/presenter/actions/getInboxCaseMessagesForUserAction.test.js b/web-client/src/presenter/actions/getInboxCaseMessagesForUserAction.test.js new file mode 100644 index 00000000000..3b92fef61f7 --- /dev/null +++ b/web-client/src/presenter/actions/getInboxCaseMessagesForUserAction.test.js @@ -0,0 +1,27 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { getInboxCaseMessagesForUserAction } from './getInboxCaseMessagesForUserAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('getInboxCaseMessagesForUserAction', () => { + const message = { + messageId: '180bfc0c-4e8e-448a-802a-8fe027be85ef', + }; + + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + applicationContext + .getUseCases() + .getInboxCaseMessagesForUserInteractor.mockReturnValue([message]); + }); + + it('returns the messages retrieved from the use case', async () => { + const results = await runAction(getInboxCaseMessagesForUserAction, { + modules: { + presenter, + }, + state: {}, + }); + expect(results.output.messages).toEqual([message]); + }); +}); diff --git a/web-client/src/presenter/actions/getMessageAction.js b/web-client/src/presenter/actions/getMessageAction.js new file mode 100644 index 00000000000..c5c5f5854a6 --- /dev/null +++ b/web-client/src/presenter/actions/getMessageAction.js @@ -0,0 +1,18 @@ +/** + * Fetches the message using the getMessage use case using the props.messageId + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext needed for getting the getCase use case + * @param {object} providers.props the cerebral props object containing props.docketNumber + * @returns {object} contains the message returned from the use case + */ +export const getMessageAction = async ({ applicationContext, props }) => { + const { messageId } = props; + const messageDetail = await applicationContext + .getUseCases() + .getCaseMessageInteractor({ + applicationContext, + messageId, + }); + return { messageDetail }; +}; diff --git a/web-client/src/presenter/actions/getMessageAction.test.js b/web-client/src/presenter/actions/getMessageAction.test.js new file mode 100644 index 00000000000..68d9e4d1a26 --- /dev/null +++ b/web-client/src/presenter/actions/getMessageAction.test.js @@ -0,0 +1,31 @@ +import { applicationContextForClient } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { getMessageAction } from './getMessageAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('getMessageAction', () => { + beforeAll(() => { + presenter.providers.applicationContext = applicationContextForClient; + }); + + it('calls the use case with props.messageId', async () => { + await runAction(getMessageAction, { + modules: { + presenter, + }, + props: { + messageId: '0fbd6b64-6e13-4984-b46b-fd74906fd2c7', + }, + }); + + expect( + applicationContextForClient.getUseCases().getCaseMessageInteractor, + ).toBeCalled(); + expect( + applicationContextForClient.getUseCases().getCaseMessageInteractor.mock + .calls[0][0], + ).toMatchObject({ + messageId: '0fbd6b64-6e13-4984-b46b-fd74906fd2c7', + }); + }); +}); diff --git a/web-client/src/presenter/actions/getOpinionTypesAction.js b/web-client/src/presenter/actions/getOpinionTypesAction.js index 47e4eed892b..40898a008fb 100644 --- a/web-client/src/presenter/actions/getOpinionTypesAction.js +++ b/web-client/src/presenter/actions/getOpinionTypesAction.js @@ -1,4 +1,4 @@ -import { Document } from '../../../../shared/src/business/entities/Document'; +import { OPINION_DOCUMENT_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; const courtIssuedEventCodes = require('../../../../shared/src/tools/courtIssuedEventCodes.json'); /** @@ -9,9 +9,7 @@ const courtIssuedEventCodes = require('../../../../shared/src/tools/courtIssuedE */ export const getOpinionTypesAction = () => { const opinionDocuments = courtIssuedEventCodes.filter(courtIssuedDocument => { - if ( - Document.OPINION_DOCUMENT_TYPES.includes(courtIssuedDocument.eventCode) - ) { + if (OPINION_DOCUMENT_TYPES.includes(courtIssuedDocument.eventCode)) { return courtIssuedDocument; } }); diff --git a/web-client/src/presenter/actions/getOutboxCaseMessagesForSectionAction.js b/web-client/src/presenter/actions/getOutboxCaseMessagesForSectionAction.js new file mode 100644 index 00000000000..ed78d72cf9b --- /dev/null +++ b/web-client/src/presenter/actions/getOutboxCaseMessagesForSectionAction.js @@ -0,0 +1,18 @@ +/** + * fetches the outbox case messages for the section + * + * @param {object} applicationContext object that contains all the context specific methods + * @returns {Promise<{CaseMessage: Array}>} a list of messages + */ +export const getOutboxCaseMessagesForSectionAction = async ({ + applicationContext, +}) => { + const messages = await applicationContext + .getUseCases() + .getOutboxCaseMessagesForSectionInteractor({ + applicationContext, + section: applicationContext.getCurrentUser().section, + }); + + return { messages }; +}; diff --git a/web-client/src/presenter/actions/getOutboxCaseMessagesForSectionAction.test.js b/web-client/src/presenter/actions/getOutboxCaseMessagesForSectionAction.test.js new file mode 100644 index 00000000000..0caa4550158 --- /dev/null +++ b/web-client/src/presenter/actions/getOutboxCaseMessagesForSectionAction.test.js @@ -0,0 +1,27 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { getOutboxCaseMessagesForSectionAction } from './getOutboxCaseMessagesForSectionAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('getOutboxCaseMessagesForSectionAction', () => { + const message = { + messageId: '180bfc0c-4e8e-448a-802a-8fe027be85ef', + }; + + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + applicationContext + .getUseCases() + .getOutboxCaseMessagesForSectionInteractor.mockReturnValue([message]); + }); + + it('returns the messages retrieved from the use case', async () => { + const results = await runAction(getOutboxCaseMessagesForSectionAction, { + modules: { + presenter, + }, + state: {}, + }); + expect(results.output.messages).toEqual([message]); + }); +}); diff --git a/web-client/src/presenter/actions/getOutboxCaseMessagesForUserAction.js b/web-client/src/presenter/actions/getOutboxCaseMessagesForUserAction.js new file mode 100644 index 00000000000..9a7abad8273 --- /dev/null +++ b/web-client/src/presenter/actions/getOutboxCaseMessagesForUserAction.js @@ -0,0 +1,18 @@ +/** + * fetches the outbox case messages for the user + * + * @param {object} applicationContext object that contains all the context specific methods + * @returns {Promise<{CaseMessage: Array}>} a list of messages + */ +export const getOutboxCaseMessagesForUserAction = async ({ + applicationContext, +}) => { + const messages = await applicationContext + .getUseCases() + .getOutboxCaseMessagesForUserInteractor({ + applicationContext, + userId: applicationContext.getCurrentUser().userId, + }); + + return { messages }; +}; diff --git a/web-client/src/presenter/actions/getOutboxCaseMessagesForUserAction.test.js b/web-client/src/presenter/actions/getOutboxCaseMessagesForUserAction.test.js new file mode 100644 index 00000000000..31d97d504d5 --- /dev/null +++ b/web-client/src/presenter/actions/getOutboxCaseMessagesForUserAction.test.js @@ -0,0 +1,27 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { getOutboxCaseMessagesForUserAction } from './getOutboxCaseMessagesForUserAction'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; + +describe('getOutboxCaseMessagesForUserAction', () => { + const message = { + messageId: '180bfc0c-4e8e-448a-802a-8fe027be85ef', + }; + + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + applicationContext + .getUseCases() + .getOutboxCaseMessagesForUserInteractor.mockReturnValue([message]); + }); + + it('returns the messages retrieved from the use case', async () => { + const results = await runAction(getOutboxCaseMessagesForUserAction, { + modules: { + presenter, + }, + state: {}, + }); + expect(results.output.messages).toEqual([message]); + }); +}); diff --git a/web-client/src/presenter/actions/getPDFForPreviewAction.js b/web-client/src/presenter/actions/getPDFForPreviewAction.js index 80eae726da5..470526894d4 100644 --- a/web-client/src/presenter/actions/getPDFForPreviewAction.js +++ b/web-client/src/presenter/actions/getPDFForPreviewAction.js @@ -5,6 +5,7 @@ import { state } from 'cerebral'; * @param {object} providers the providers object * @param {object} providers.applicationContext the application context * @param {Function} providers.props used for getting caseId and documentId + * @returns {Promise} pdf file object for preview */ export const getPDFForPreviewAction = async ({ applicationContext, diff --git a/web-client/src/presenter/actions/getUserByIdAction.js b/web-client/src/presenter/actions/getUserByIdAction.js deleted file mode 100644 index 47764b346bc..00000000000 --- a/web-client/src/presenter/actions/getUserByIdAction.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * gets the user by the userId in props - * - * @param {object} providers the providers object - * @param {object} providers.applicationContext the application context used for getting the getUser use case - * @param {object} providers.props the cerebral props object used for getting the props.user - * @returns {object} the user - */ -export const getUserByIdAction = async ({ applicationContext, props }) => { - const user = await applicationContext - .getUseCases() - .getUserByIdInteractor({ applicationContext, userId: props.userId }); - return { user }; -}; diff --git a/web-client/src/presenter/actions/getUserByIdAction.test.js b/web-client/src/presenter/actions/getUserByIdAction.test.js deleted file mode 100644 index 0d3fa558b3b..00000000000 --- a/web-client/src/presenter/actions/getUserByIdAction.test.js +++ /dev/null @@ -1,30 +0,0 @@ -import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; -import { getUserByIdAction } from './getUserByIdAction'; -import { presenter } from '../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('getUserByIdAction', () => { - beforeAll(() => { - presenter.providers.applicationContext = applicationContext; - applicationContext.getUseCases().getUserByIdInteractor.mockReturnValue({ - role: 'privatePractitioner', - userId: '123', - }); - }); - - it('should call the user and return the user from the use case', async () => { - const results = await runAction(getUserByIdAction, { - modules: { - presenter, - }, - props: { - userId: '123', - }, - }); - - expect(results.output.user).toEqual({ - role: 'privatePractitioner', - userId: '123', - }); - }); -}); diff --git a/web-client/src/presenter/actions/navigateToFirstResultCaseDetailAction.js b/web-client/src/presenter/actions/navigateToFirstResultCaseDetailAction.js deleted file mode 100644 index 9a41c7031a4..00000000000 --- a/web-client/src/presenter/actions/navigateToFirstResultCaseDetailAction.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * changes the route to view the case-detail of the caseId of props.caseId - * - * @param {object} providers the providers object - * @param {object} providers.router the riot.router object that is used for changing the route - * @param {object} providers.props the cerebral props object - * @returns {Promise} async action - */ -export const navigateToFirstResultCaseDetailAction = async ({ - props, - router, -}) => { - await router.route(`/case-detail/${props.searchResults[0].docketNumber}`); -}; diff --git a/web-client/src/presenter/actions/navigateToFirstResultCaseDetailAction.test.js b/web-client/src/presenter/actions/navigateToFirstResultCaseDetailAction.test.js deleted file mode 100644 index 63fa8ffe334..00000000000 --- a/web-client/src/presenter/actions/navigateToFirstResultCaseDetailAction.test.js +++ /dev/null @@ -1,37 +0,0 @@ -import { navigateToFirstResultCaseDetailAction } from './navigateToFirstResultCaseDetailAction'; -import { presenter } from '../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('navigateToFirstResultCaseDetailAction', () => { - let routeStub; - - beforeAll(() => { - routeStub = jest.fn(); - - presenter.providers.router = { - route: routeStub, - }; - }); - - it('navigates to case detail url for the provided docket number', async () => { - const mockArgs = { - modules: { - presenter, - }, - props: { - searchResults: [ - { - docketNumber: 1234, - }, - ], - }, - }; - - await runAction(navigateToFirstResultCaseDetailAction, mockArgs); - - expect(routeStub).toHaveBeenCalled(); - expect(routeStub.mock.calls[0][0]).toEqual( - `/case-detail/${mockArgs.props.searchResults[0].docketNumber}`, - ); - }); -}); diff --git a/web-client/src/presenter/actions/navigateToOrdersNeededAction.js b/web-client/src/presenter/actions/navigateToOrdersNeededAction.js deleted file mode 100644 index d1cbe0c4b18..00000000000 --- a/web-client/src/presenter/actions/navigateToOrdersNeededAction.js +++ /dev/null @@ -1,16 +0,0 @@ -import { state } from 'cerebral'; - -/** - * changes the route to view the orders needed summary for a given petition - * - * @param {object} providers the providers object - * @param {object} providers.router the riot.router object that is used for changing the route - * @param {object} providers.props the cerebral props that contain the props.docketNumber - * @returns {Promise} async action - */ -export const navigateToOrdersNeededAction = async ({ get, props, router }) => { - const docketNumber = props.docketNumber || get(state.caseDetail.docketNumber); - if (docketNumber) { - await router.route(`/case-detail/${docketNumber}/orders-needed`); - } -}; diff --git a/web-client/src/presenter/actions/navigateToOrdersNeededAction.test.js b/web-client/src/presenter/actions/navigateToOrdersNeededAction.test.js deleted file mode 100644 index 8d089dc52bb..00000000000 --- a/web-client/src/presenter/actions/navigateToOrdersNeededAction.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import { navigateToOrdersNeededAction } from './navigateToOrdersNeededAction'; -import { presenter } from '../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('navigateToOrdersNeededAction', () => { - let routeStub; - - beforeAll(() => { - routeStub = jest.fn(); - - presenter.providers.router = { - route: routeStub, - }; - }); - - it('navigates to orders needed summary url when given docketNumber', async () => { - await runAction(navigateToOrdersNeededAction, { - modules: { - presenter, - }, - props: { - docketNumber: '123-19', - }, - }); - - expect(routeStub.mock.calls.length).toBe(1); - expect(routeStub.mock.calls[0][0]).toBe( - '/case-detail/123-19/orders-needed', - ); - }); - - it('does not navigate to orders needed summary url when there is no docketNumber', async () => { - await runAction(navigateToOrdersNeededAction, { - modules: { - presenter, - }, - }); - - expect(routeStub.mock.calls.length).toBe(0); - }); -}); diff --git a/web-client/src/presenter/actions/navigateToPdfPreviewAction.js b/web-client/src/presenter/actions/navigateToPdfPreviewAction.js deleted file mode 100644 index 83813fadc78..00000000000 --- a/web-client/src/presenter/actions/navigateToPdfPreviewAction.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * changes the route to the simple pdf preview page - * - * @param {object} providers the providers object - * @param {object} providers.router the riot.router object that is used for changing the route - * @returns {Promise} async action - */ -export const navigateToPdfPreviewAction = async ({ router }) => { - await router.route('/pdf-preview'); -}; diff --git a/web-client/src/presenter/actions/navigateToPdfPreviewAction.test.js b/web-client/src/presenter/actions/navigateToPdfPreviewAction.test.js deleted file mode 100644 index 090912c1db8..00000000000 --- a/web-client/src/presenter/actions/navigateToPdfPreviewAction.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import { navigateToPdfPreviewAction } from './navigateToPdfPreviewAction'; -import { presenter } from '../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('navigateToPdfPreviewAction', () => { - let routeStub; - - beforeAll(() => { - routeStub = jest.fn(); - - presenter.providers.router = { - route: routeStub, - }; - }); - - it('should navigate to the pdf preview page', async () => { - await runAction(navigateToPdfPreviewAction, { - modules: { - presenter, - }, - }); - - expect(routeStub).toHaveBeenLastCalledWith('/pdf-preview'); - }); -}); diff --git a/web-client/src/presenter/actions/openPdfPreviewInNewTabAction.js b/web-client/src/presenter/actions/openPdfPreviewInNewTabAction.js deleted file mode 100644 index 64e5175b493..00000000000 --- a/web-client/src/presenter/actions/openPdfPreviewInNewTabAction.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * opens the pdfUrl from props in a new tab - * - * @param {object} providers the providers object - * @param {object} providers.props the cerebral props object - * @param {object} providers.router the riot.router object that is used for changing the route - */ -export const openPdfPreviewInNewTabAction = ({ props, router }) => { - const { pdfUrl } = props; - - router.openInNewTab(pdfUrl); -}; diff --git a/web-client/src/presenter/actions/openPdfPreviewInNewTabAction.test.js b/web-client/src/presenter/actions/openPdfPreviewInNewTabAction.test.js deleted file mode 100644 index 64a8bb4c0ab..00000000000 --- a/web-client/src/presenter/actions/openPdfPreviewInNewTabAction.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import { openPdfPreviewInNewTabAction } from './openPdfPreviewInNewTabAction'; -import { presenter } from '../presenter-mock'; -import { runAction } from 'cerebral/test'; - -describe('openPdfPreviewInNewTabAction', () => { - const openInNewTabMock = jest.fn(); - - beforeAll(() => { - presenter.providers.router = { - openInNewTab: openInNewTabMock, - }; - }); - - it('should open the pdfUrl from props in a new tab', () => { - runAction(openPdfPreviewInNewTabAction, { - modules: { - presenter, - }, - props: { pdfUrl: 'http://www.example.com' }, - }); - - expect(openInNewTabMock).toBeCalled(); - expect(openInNewTabMock).toBeCalledWith('http://www.example.com'); - }); -}); diff --git a/web-client/src/presenter/actions/rescanBatchAction.test.js b/web-client/src/presenter/actions/rescanBatchAction.test.js index b39fb4c390f..e8450ca5c38 100644 --- a/web-client/src/presenter/actions/rescanBatchAction.test.js +++ b/web-client/src/presenter/actions/rescanBatchAction.test.js @@ -1,12 +1,10 @@ -import { Scan } from '../../../../shared/src/business/entities/Scan'; +import { SCAN_MODES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../presenter-mock'; import { rescanBatchAction } from './rescanBatchAction'; import { runAction } from 'cerebral/test'; describe('rescanBatchAction', () => { - const { SCAN_MODES } = Scan; - const successStub = jest.fn(); const errorStub = jest.fn(); diff --git a/web-client/src/presenter/actions/resetContactsAction.test.js b/web-client/src/presenter/actions/resetContactsAction.test.js index 072f12f13c5..b57d9d48c14 100644 --- a/web-client/src/presenter/actions/resetContactsAction.test.js +++ b/web-client/src/presenter/actions/resetContactsAction.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../presenter-mock'; import { resetContactsAction } from './resetContactsAction'; @@ -24,7 +24,7 @@ describe('resetContactsAction', () => { state: 'AL', zip: '12345', }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -33,7 +33,7 @@ describe('resetContactsAction', () => { countryType: 'domestic', email: 'test@example.com', }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); }); @@ -52,7 +52,7 @@ describe('resetContactsAction', () => { phone: '1234567890', zip: '12345', }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -61,7 +61,7 @@ describe('resetContactsAction', () => { countryType: 'domestic', email: 'test@example.com', }, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }); }); @@ -88,7 +88,7 @@ describe('resetContactsAction', () => { state: 'AL', zip: '12345', }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }, }, }); @@ -98,7 +98,7 @@ describe('resetContactsAction', () => { email: 'test@example.com', }, contactSecondary: { countryType: 'domestic' }, - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }); }); }); diff --git a/web-client/src/presenter/actions/runPathForUserRoleAction.test.js b/web-client/src/presenter/actions/runPathForUserRoleAction.test.js index 9f70d17319f..52e9462832c 100644 --- a/web-client/src/presenter/actions/runPathForUserRoleAction.test.js +++ b/web-client/src/presenter/actions/runPathForUserRoleAction.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -37,7 +37,7 @@ describe('runPathForUserRoleAction', () => { it('should return the petitioner path for user role petitioner', async () => { presenter.providers.applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); await runAction(runPathForUserRoleAction, { modules: { @@ -50,7 +50,7 @@ describe('runPathForUserRoleAction', () => { it('should return the privatePractitioner path for user role privatePractitioner', async () => { presenter.providers.applicationContext.getCurrentUser = () => ({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); await runAction(runPathForUserRoleAction, { modules: { @@ -63,7 +63,7 @@ describe('runPathForUserRoleAction', () => { it('should return the irsPractitioner path for user role irsPractitioner', async () => { presenter.providers.applicationContext.getCurrentUser = () => ({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }); await runAction(runPathForUserRoleAction, { modules: { @@ -76,7 +76,7 @@ describe('runPathForUserRoleAction', () => { it('should return the petitionsclerk path for user role petitionsclerk', async () => { presenter.providers.applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }); await runAction(runPathForUserRoleAction, { modules: { @@ -89,7 +89,7 @@ describe('runPathForUserRoleAction', () => { it('should return the docketclerk path for user role docketclerk', async () => { presenter.providers.applicationContext.getCurrentUser = () => ({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }); await runAction(runPathForUserRoleAction, { modules: { @@ -102,7 +102,7 @@ describe('runPathForUserRoleAction', () => { it('should return the judge path for user role judge', async () => { presenter.providers.applicationContext.getCurrentUser = () => ({ - role: User.ROLES.judge, + role: ROLES.judge, }); await runAction(runPathForUserRoleAction, { modules: { diff --git a/web-client/src/presenter/actions/saveCaseDetailInternalEditAction.test.js b/web-client/src/presenter/actions/saveCaseDetailInternalEditAction.test.js index d82d4bf204f..5ec0b09bac8 100644 --- a/web-client/src/presenter/actions/saveCaseDetailInternalEditAction.test.js +++ b/web-client/src/presenter/actions/saveCaseDetailInternalEditAction.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; +import { CASE_STATUS_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../presenter-mock'; @@ -14,7 +14,7 @@ describe('saveCaseDetailInternalEditAction', () => { const caseDetail = { ...MOCK_CASE, createdAt: '2019-03-01T21:40:46.415Z', - status: Case.STATUS_TYPES.generalDocketReadyForTrial, + status: CASE_STATUS_TYPES.generalDocketReadyForTrial, }; applicationContext .getUseCases() @@ -38,7 +38,7 @@ describe('saveCaseDetailInternalEditAction', () => { it('should not call the updateCaseTrialSortTags use case if case status is not ready for trial', async () => { const caseDetail = { ...MOCK_CASE, - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }; applicationContext .getUseCases() diff --git a/web-client/src/presenter/actions/scannerShutdownAction.js b/web-client/src/presenter/actions/scannerShutdownAction.js deleted file mode 100644 index 315a5969a2b..00000000000 --- a/web-client/src/presenter/actions/scannerShutdownAction.js +++ /dev/null @@ -1,22 +0,0 @@ -import { state } from 'cerebral'; - -/** - * removes third-party scanner scripts from the DOM and sets associated state - * - * @param {object} providers the providers object - * @param {object} providers.store the cerebral store used for setting state.scanner - * @param {Function} providers.get the cerebral get helper function - * @returns {void} - */ - -export const scannerShutdownAction = ({ get, store }) => { - const dynamScriptClass = get(state.scanner.dynamScriptClass); - if (dynamScriptClass) { - const injectedScripts = Array.from( - document.getElementsByClassName(dynamScriptClass), - ); - injectedScripts.forEach(scriptEl => scriptEl.remove()); - } - store.set(state.scanner.initiateScriptLoaded, false); - store.set(state.scanner.configScriptLoaded, false); -}; diff --git a/web-client/src/presenter/actions/scannerShutdownAction.test.js b/web-client/src/presenter/actions/scannerShutdownAction.test.js deleted file mode 100644 index 29aaa72dd14..00000000000 --- a/web-client/src/presenter/actions/scannerShutdownAction.test.js +++ /dev/null @@ -1,42 +0,0 @@ -import { JSDOM } from 'jsdom'; -import { runAction } from 'cerebral/test'; -import { scannerShutdownAction } from './scannerShutdownAction'; - -const dynamScriptClass = 'dynam-scanner-injection'; - -const jsdom = new JSDOM( - ``, -); - -global.document = jsdom.window.document; -global.window = jsdom.window; - -describe('scannerShutdownAction', () => { - it('removes injected scanner scripts', async () => { - await runAction(scannerShutdownAction, { - state: { - scanner: { - dynamScriptClass: dynamScriptClass, - }, - }, - }); - const scriptElements = Array.from( - document.getElementsByClassName(dynamScriptClass), - ); - expect(scriptElements.length).toEqual(0); - }); - - it('sets the scanner init and config script load states to false', async () => { - const result = await runAction(scannerShutdownAction, { - state: { - scanner: { - configScriptLoaded: true, - initiateScriptLoaded: true, - }, - }, - }); - - expect(result.state.scanner.configScriptLoaded).toEqual(false); - expect(result.state.scanner.initiateScriptLoaded).toEqual(false); - }); -}); diff --git a/web-client/src/presenter/actions/setCanvasForPDFSigningAction.js b/web-client/src/presenter/actions/setCanvasForPDFSigningAction.js deleted file mode 100644 index fbe9d2f858f..00000000000 --- a/web-client/src/presenter/actions/setCanvasForPDFSigningAction.js +++ /dev/null @@ -1,14 +0,0 @@ -import { state } from 'cerebral'; - -/** - * given a PDF document, returns a pdf.js object - * - * @param {object} providers the providers object - * @param {Function} providers.props used for getting the canvas ref - * @param {Function} providers.store the cerebral store used for setting state.pdfForSigning.canvas - - */ -export const setCanvasForPDFSigningAction = async ({ props, store }) => { - const { canvasRef } = props; - store.set(state.pdfForSigning.canvas, canvasRef); -}; diff --git a/web-client/src/presenter/actions/setCanvasForPDFSigningAction.test.js b/web-client/src/presenter/actions/setCanvasForPDFSigningAction.test.js deleted file mode 100644 index ff5eba44bf3..00000000000 --- a/web-client/src/presenter/actions/setCanvasForPDFSigningAction.test.js +++ /dev/null @@ -1,20 +0,0 @@ -import { runAction } from 'cerebral/test'; -import { setCanvasForPDFSigningAction } from './setCanvasForPDFSigningAction'; - -describe('setCanvasForPDFSigningAction', () => { - it('Sets state.pdfForSigning.canvas', async () => { - const newState = 'someRef'; - const result = await runAction(setCanvasForPDFSigningAction, { - props: { - canvasRef: newState, - }, - state: { - pdfForSigning: { - signatureData: null, - }, - }, - }); - - expect(result.state.pdfForSigning.canvas).toEqual(newState); - }); -}); diff --git a/web-client/src/presenter/actions/setCaseDetailPageTabAction.js b/web-client/src/presenter/actions/setCaseDetailPageTabAction.js deleted file mode 100644 index 3955a6c5fc1..00000000000 --- a/web-client/src/presenter/actions/setCaseDetailPageTabAction.js +++ /dev/null @@ -1,23 +0,0 @@ -import { state } from 'cerebral'; - -/** - * Sets the currentViewMetadata.caseDetail.primaryTab view. - * - * @param {object} providers the providers object - * @param {object} providers.store the cerebral store object used for setting workQueueToDisplay - * @param {object} providers.props.tab the tab to display - */ -export const setCaseDetailPageTabAction = ({ props, store }) => { - if (props.isSecondary) { - store.set( - state.currentViewMetadata.caseDetail.primaryTab, - 'caseInformation', - ); - store.set( - state.currentViewMetadata.caseDetail.caseInformationTab, - props.tab, - ); - } else { - store.set(state.currentViewMetadata.caseDetail.primaryTab, props.tab); - } -}; diff --git a/web-client/src/presenter/actions/setCaseDetailPageTabActionGenerator.js b/web-client/src/presenter/actions/setCaseDetailPageTabActionGenerator.js new file mode 100644 index 00000000000..36c86f20e50 --- /dev/null +++ b/web-client/src/presenter/actions/setCaseDetailPageTabActionGenerator.js @@ -0,0 +1,25 @@ +import { state } from 'cerebral'; + +/** + * Sets the currentViewMetadata.caseDetail.primaryTab view. + * + * @param {string} tab the tab to display + * @returns {Function} a function that sets the tab name + */ +export const setCaseDetailPageTabActionGenerator = tab => { + return ({ props, store }) => { + const tabName = tab || props.tab; + if (props.isSecondary) { + store.set( + state.currentViewMetadata.caseDetail.primaryTab, + 'caseInformation', + ); + store.set( + state.currentViewMetadata.caseDetail.caseInformationTab, + tabName, + ); + } else { + store.set(state.currentViewMetadata.caseDetail.primaryTab, tabName); + } + }; +}; diff --git a/web-client/src/presenter/actions/setCaseDetailPageTabAction.test.js b/web-client/src/presenter/actions/setCaseDetailPageTabActionGenerator.test.js similarity index 66% rename from web-client/src/presenter/actions/setCaseDetailPageTabAction.test.js rename to web-client/src/presenter/actions/setCaseDetailPageTabActionGenerator.test.js index 1f878db11d7..de73c1104e2 100644 --- a/web-client/src/presenter/actions/setCaseDetailPageTabAction.test.js +++ b/web-client/src/presenter/actions/setCaseDetailPageTabActionGenerator.test.js @@ -1,9 +1,9 @@ import { runAction } from 'cerebral/test'; -import { setCaseDetailPageTabAction } from './setCaseDetailPageTabAction'; +import { setCaseDetailPageTabActionGenerator } from './setCaseDetailPageTabActionGenerator'; describe('setDefaultDocumentDetailTab', () => { it('sets state.currentViewMetadata.caseDetail.primaryTab to the passed in props.tab', async () => { - const { state } = await runAction(setCaseDetailPageTabAction, { + const { state } = await runAction(setCaseDetailPageTabActionGenerator(), { props: { tab: 'DocketRecord', }, @@ -14,7 +14,7 @@ describe('setDefaultDocumentDetailTab', () => { }); it('sets state.currentViewMetadata.caseDetail.primaryTab to caseInformation and state.currentViewMetadata.caseDetail.caseInformationTab to the passed in props.tab if isSecondary is true', async () => { - const { state } = await runAction(setCaseDetailPageTabAction, { + const { state } = await runAction(setCaseDetailPageTabActionGenerator(), { props: { isSecondary: true, tab: 'overview', @@ -27,4 +27,16 @@ describe('setDefaultDocumentDetailTab', () => { 'overview', ); }); + + it('sets state.currentViewMetadata.caseDetail.primaryTab to the passed in parameter value if isSecondary is true', async () => { + const { state } = await runAction( + setCaseDetailPageTabActionGenerator('correspondence'), + { + props: {}, + }, + ); + expect(state.currentViewMetadata.caseDetail.primaryTab).toEqual( + 'correspondence', + ); + }); }); diff --git a/web-client/src/presenter/actions/setCaseIdFromSearchAction.js b/web-client/src/presenter/actions/setCaseIdFromSearchAction.js index 532e26cf620..1e9776d557c 100644 --- a/web-client/src/presenter/actions/setCaseIdFromSearchAction.js +++ b/web-client/src/presenter/actions/setCaseIdFromSearchAction.js @@ -1,7 +1,10 @@ import { state } from 'cerebral'; -const docketNumberMatcher = /^(\d{3,5}-\d{2})[XPRWSL]?L?(.*)$/; +const docketNumberMatcher = /^(\d{3,5}-\d{2})[XPRWSL]?L?(.*)$/i; export const trimDocketNumberSearch = searchTerm => { + if (!searchTerm) { + return ''; + } const match = docketNumberMatcher.exec(searchTerm.trim()); const docketNumber = match && match.length > 1 && match[2] === '' ? match[1] : searchTerm; diff --git a/web-client/src/presenter/actions/setCaseMessagesAction.js b/web-client/src/presenter/actions/setCaseMessagesAction.js new file mode 100644 index 00000000000..5cec449b883 --- /dev/null +++ b/web-client/src/presenter/actions/setCaseMessagesAction.js @@ -0,0 +1,13 @@ +import { state } from 'cerebral'; + +/** + * sets the state.messages based on the props.messages passed in. + * + * @param {object} providers the providers object + * @param {object} providers.store the cerebral store + * @param {object} providers.props the cerebral props object + * @returns {undefined} + */ +export const setCaseMessagesAction = ({ props, store }) => { + store.set(state.messages, props.messages); +}; diff --git a/web-client/src/presenter/actions/setCaseMessagesAction.test.js b/web-client/src/presenter/actions/setCaseMessagesAction.test.js new file mode 100644 index 00000000000..6ae6d51914d --- /dev/null +++ b/web-client/src/presenter/actions/setCaseMessagesAction.test.js @@ -0,0 +1,16 @@ +import { runAction } from 'cerebral/test'; +import { setCaseMessagesAction } from './setCaseMessagesAction'; + +describe('setCaseMessagesAction', () => { + const message = { + messageId: '180bfc0c-4e8e-448a-802a-8fe027be85ef', + }; + + it('sets props.messages on state.messages', async () => { + const results = await runAction(setCaseMessagesAction, { + props: { messages: [message] }, + state: {}, + }); + expect(results.state.messages).toEqual([message]); + }); +}); diff --git a/web-client/src/presenter/actions/setCaseOnFormUsingStateAction.js b/web-client/src/presenter/actions/setCaseOnFormUsingStateAction.js index 679cba89018..bacd0faebb3 100644 --- a/web-client/src/presenter/actions/setCaseOnFormUsingStateAction.js +++ b/web-client/src/presenter/actions/setCaseOnFormUsingStateAction.js @@ -6,6 +6,7 @@ import { state } from 'cerebral'; * @param {object} providers the providers object * @param {Function} providers.get the cerebral get function * @param {object} providers.store the cerebral store + * @returns {object} caseDetail onto the props stream */ export const setCaseOnFormUsingStateAction = async ({ get, store }) => { const caseDetail = get(state.caseDetail); diff --git a/web-client/src/presenter/actions/setCaseTypeToDisplayAction.js b/web-client/src/presenter/actions/setCaseTypeToDisplayAction.js new file mode 100644 index 00000000000..70e9b63e3c8 --- /dev/null +++ b/web-client/src/presenter/actions/setCaseTypeToDisplayAction.js @@ -0,0 +1,13 @@ +import { state } from 'cerebral'; + +/** + * sets the case type to display to open or closed + * + * @param {object} providers the providers object + * @param {object} providers.get the cerebral get function + * @param {object} providers.store the cerebral store object + * @returns {void} + */ +export const setCaseTypeToDisplayAction = ({ props, store }) => { + store.set(state.openClosedCases.caseType, props.tabName); +}; diff --git a/web-client/src/presenter/actions/setCaseTypeToDisplayAction.test.js b/web-client/src/presenter/actions/setCaseTypeToDisplayAction.test.js new file mode 100644 index 00000000000..fc96ab66c21 --- /dev/null +++ b/web-client/src/presenter/actions/setCaseTypeToDisplayAction.test.js @@ -0,0 +1,18 @@ +import { runAction } from 'cerebral/test'; +import { setCaseTypeToDisplayAction } from './setCaseTypeToDisplayAction'; + +describe('setCaseTypeToDisplayAction', () => { + it('sets state.openClosedCases.caseType to the value of props.tabName', async () => { + const result = await runAction(setCaseTypeToDisplayAction, { + props: { + tabName: 'Open', + }, + }); + + expect(result.state).toMatchObject({ + openClosedCases: { + caseType: 'Open', + }, + }); + }); +}); diff --git a/web-client/src/presenter/actions/setCasesAction.js b/web-client/src/presenter/actions/setCasesAction.js index a91bd58b4c4..2303fc4ce85 100644 --- a/web-client/src/presenter/actions/setCasesAction.js +++ b/web-client/src/presenter/actions/setCasesAction.js @@ -1,12 +1,13 @@ import { state } from 'cerebral'; /** - * sets the state.cases based on the props.caseList passed in + * sets the state.openCases and state.closedCases based on the props.openCaseList and props.closedCaseList passed in * * @param {object} providers the providers object - * @param {object} providers.store the cerebral store used for setting the state.cases - * @param {object} providers.props the cerebral props object used for passing the props.caseList + * @param {object} providers.store the cerebral store used for setting the state.openCases and state.closedCases + * @param {object} providers.props the cerebral props object used for passing the props.closedCaseList and props.openCaseList */ export const setCasesAction = ({ props, store }) => { - store.set(state.cases, props.caseList); + store.set(state.openCases, props.openCaseList); + store.set(state.closedCases, props.closedCaseList); }; diff --git a/web-client/src/presenter/actions/setCasesAction.test.js b/web-client/src/presenter/actions/setCasesAction.test.js index 44be4444991..1fdb312a835 100644 --- a/web-client/src/presenter/actions/setCasesAction.test.js +++ b/web-client/src/presenter/actions/setCasesAction.test.js @@ -2,10 +2,18 @@ import { runAction } from 'cerebral/test'; import { setCasesAction } from './setCasesAction'; describe('setCasesAction', () => { - it('sets state.cases from props.caseList', async () => { + it('sets state.openCases from props.openCaseList and state.closedCases from props.closedCaseList', async () => { const { state } = await runAction(setCasesAction, { props: { - caseList: [ + closedCaseList: [ + { + caseId: '1234', + }, + { + caseId: '2345', + }, + ], + openCaseList: [ { caseId: '1234', }, @@ -15,11 +23,18 @@ describe('setCasesAction', () => { ], }, state: { - cases: [], + closedCases: [], + openCases: [], }, }); - expect(state.cases).toEqual([ + expect(state.openCases).toEqual([ + { + caseId: '1234', + }, + { caseId: '2345' }, + ]); + expect(state.closedCases).toEqual([ { caseId: '1234', }, diff --git a/web-client/src/presenter/actions/setDefaultCaseTypeToDisplayAction.js b/web-client/src/presenter/actions/setDefaultCaseTypeToDisplayAction.js new file mode 100644 index 00000000000..e39eceeb955 --- /dev/null +++ b/web-client/src/presenter/actions/setDefaultCaseTypeToDisplayAction.js @@ -0,0 +1,17 @@ +import { state } from 'cerebral'; + +/** + * sets the default case type to display to open + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the applicationContext + * @param {object} providers.store the cerebral store object + * @returns {void} + */ +export const setDefaultCaseTypeToDisplayAction = ({ + applicationContext, + store, +}) => { + const { EXTERNAL_USER_DASHBOARD_TABS } = applicationContext.getConstants(); + store.set(state.openClosedCases.caseType, EXTERNAL_USER_DASHBOARD_TABS.OPEN); +}; diff --git a/web-client/src/presenter/actions/setDefaultCaseTypeToDisplayAction.test.js b/web-client/src/presenter/actions/setDefaultCaseTypeToDisplayAction.test.js new file mode 100644 index 00000000000..b6a706211c9 --- /dev/null +++ b/web-client/src/presenter/actions/setDefaultCaseTypeToDisplayAction.test.js @@ -0,0 +1,23 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { setDefaultCaseTypeToDisplayAction } from './setDefaultCaseTypeToDisplayAction'; + +describe('setDefaultCaseTypeToDisplayAction', () => { + const { EXTERNAL_USER_DASHBOARD_TABS } = applicationContext.getConstants(); + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + }); + + it('sets the default value for state.openClosedCases.caseType to "Open"', async () => { + const result = await runAction(setDefaultCaseTypeToDisplayAction, { + modules: { + presenter, + }, + }); + + expect(result.state.openClosedCases.caseType).toEqual( + EXTERNAL_USER_DASHBOARD_TABS.OPEN, + ); + }); +}); diff --git a/web-client/src/presenter/actions/setDefaultFormForAddDeficiencySatisticsAction.js b/web-client/src/presenter/actions/setDefaultFormForAddDeficiencySatisticsAction.js new file mode 100644 index 00000000000..4e9a860f962 --- /dev/null +++ b/web-client/src/presenter/actions/setDefaultFormForAddDeficiencySatisticsAction.js @@ -0,0 +1,13 @@ +import { state } from 'cerebral'; + +/** + * sets the state.form yearOrPeriod + * + * @param {object} providers the providers object + * @param {object} providers.store the cerebral store used for setting state.workItem + */ +export const setDefaultFormForAddDeficiencySatisticsAction = ({ store }) => { + store.set(state.form, { + yearOrPeriod: 'Year', + }); +}; diff --git a/web-client/src/presenter/actions/setEditDeficiencyStatisticFormAction.js b/web-client/src/presenter/actions/setEditDeficiencyStatisticFormAction.js new file mode 100644 index 00000000000..3b631b2fb7f --- /dev/null +++ b/web-client/src/presenter/actions/setEditDeficiencyStatisticFormAction.js @@ -0,0 +1,41 @@ +import { state } from 'cerebral'; + +/** + * sets the state.form using the statistic from the caseDetail + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context + * @param {Function} providers.get the cerebral get function + * @param {object} providers.props the cerebral props object + * @param {object} providers.store the cerebral store + */ +export const setEditDeficiencyStatisticFormAction = ({ + applicationContext, + get, + props, + store, +}) => { + const { statistics } = get(state.caseDetail); + const { statisticId } = props; + + const statisticToEdit = statistics.find( + statistic => statistic.statisticId === statisticId, + ); + + if (statisticToEdit.lastDateOfPeriod) { + const deconstructedLastDateOfPeriod = applicationContext + .getUtilities() + .deconstructDate(statisticToEdit.lastDateOfPeriod); + + if (deconstructedLastDateOfPeriod) { + statisticToEdit.lastDateOfPeriodMonth = + deconstructedLastDateOfPeriod.month; + statisticToEdit.lastDateOfPeriodDay = deconstructedLastDateOfPeriod.day; + statisticToEdit.lastDateOfPeriodYear = deconstructedLastDateOfPeriod.year; + } + } + + store.set(state.form, { + ...statisticToEdit, + }); +}; diff --git a/web-client/src/presenter/actions/setEditDeficiencyStatisticFormAction.test.js b/web-client/src/presenter/actions/setEditDeficiencyStatisticFormAction.test.js new file mode 100644 index 00000000000..b6a3853e166 --- /dev/null +++ b/web-client/src/presenter/actions/setEditDeficiencyStatisticFormAction.test.js @@ -0,0 +1,57 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { setEditDeficiencyStatisticFormAction } from './setEditDeficiencyStatisticFormAction'; + +describe('setEditDeficiencyStatisticFormAction', () => { + beforeAll(() => { + presenter.providers.applicationContext = applicationContext; + }); + + it('sets the statistic with the given index onto the form from the state.caseDetail', async () => { + const result = await runAction(setEditDeficiencyStatisticFormAction, { + modules: { presenter }, + props: { statisticId: '771997ff-ff16-4de6-8143-2b10e6eafe98' }, + state: { + caseDetail: { + statistics: [ + { + irsTotalPenalties: 1, + statisticId: '771997ff-ff16-4de6-8143-2b10e6eafe98', + }, + ], + }, + }, + }); + expect(result.state.form).toEqual({ + irsTotalPenalties: 1, + statisticId: '771997ff-ff16-4de6-8143-2b10e6eafe98', + }); + }); + + it('sets the statistic with the given index onto the form from the state.caseDetail with a deconstructed lastDateOfPeriod', async () => { + const result = await runAction(setEditDeficiencyStatisticFormAction, { + modules: { presenter }, + props: { statisticId: '771997ff-ff16-4de6-8143-2b10e6eafe98' }, + state: { + caseDetail: { + statistics: [ + { + irsTotalPenalties: 1, + lastDateOfPeriod: '2019-03-01T21:40:46.415Z', + statisticId: '771997ff-ff16-4de6-8143-2b10e6eafe98', + }, + ], + }, + }, + }); + expect(result.state.form).toEqual({ + irsTotalPenalties: 1, + lastDateOfPeriod: '2019-03-01T21:40:46.415Z', + lastDateOfPeriodDay: '1', + lastDateOfPeriodMonth: '3', + lastDateOfPeriodYear: '2019', + statisticId: '771997ff-ff16-4de6-8143-2b10e6eafe98', + }); + }); +}); diff --git a/web-client/src/presenter/actions/setEditOtherStatisticsFormAction.js b/web-client/src/presenter/actions/setEditOtherStatisticsFormAction.js new file mode 100644 index 00000000000..15956bf1a12 --- /dev/null +++ b/web-client/src/presenter/actions/setEditOtherStatisticsFormAction.js @@ -0,0 +1,18 @@ +import { state } from 'cerebral'; + +/** + * sets the state.form using the damages and litigationCosts from the caseDetail + * + * @param {object} providers the providers object + * @param {object} providers.get the cerebral get function + * @param {object} providers.store the cerebral store used for setting the state.caseDetail + */ +export const setEditOtherStatisticsFormAction = ({ get, store }) => { + const { damages, litigationCosts } = get(state.caseDetail); + + store.set(state.form, { + damages, + isEditing: true, + litigationCosts, + }); +}; diff --git a/web-client/src/presenter/actions/setEditOtherStatisticsFormAction.test.js b/web-client/src/presenter/actions/setEditOtherStatisticsFormAction.test.js new file mode 100644 index 00000000000..fec322f2ae6 --- /dev/null +++ b/web-client/src/presenter/actions/setEditOtherStatisticsFormAction.test.js @@ -0,0 +1,20 @@ +import { runAction } from 'cerebral/test'; +import { setEditOtherStatisticsFormAction } from './setEditOtherStatisticsFormAction'; + +describe('setEditOtherStatisticsFormAction', () => { + it('sets the damages and litigationCosts onto the form from the state.caseDetail', async () => { + const result = await runAction(setEditOtherStatisticsFormAction, { + state: { + caseDetail: { + damages: 1, + litigationCosts: 1, + }, + }, + }); + expect(result.state.form).toEqual({ + damages: 1, + isEditing: true, + litigationCosts: 1, + }); + }); +}); diff --git a/web-client/src/presenter/actions/setFormValueAction.js b/web-client/src/presenter/actions/setFormValueAction.js index 38dee314f20..bdbf989820e 100644 --- a/web-client/src/presenter/actions/setFormValueAction.js +++ b/web-client/src/presenter/actions/setFormValueAction.js @@ -1,7 +1,7 @@ import { state } from 'cerebral'; /** - * sets the state.form to the pros.value passed in + * sets the state.form to the props.value passed in * * @param {object} providers the providers object * @param {object} providers.store the cerebral store diff --git a/web-client/src/presenter/actions/setFullUserOnFormAction.js b/web-client/src/presenter/actions/setFullUserOnFormAction.js deleted file mode 100644 index 3a93f744f50..00000000000 --- a/web-client/src/presenter/actions/setFullUserOnFormAction.js +++ /dev/null @@ -1,14 +0,0 @@ -import { state } from 'cerebral'; - -/** - * sets the state.form to the entire props.user passed in (used for internal users editing users, including private fields) - * - * @param {object} providers the providers object - * @param {object} providers.props the cerebral props object used for getting the props.user - * @param {object} providers.store the cerebral store used for setting state.form - * @returns {Promise} async action - * - */ -export const setFullUserOnFormAction = async ({ props, store }) => { - store.set(state.form, props.user); -}; diff --git a/web-client/src/presenter/actions/setFullUserOnFormAction.test.js b/web-client/src/presenter/actions/setFullUserOnFormAction.test.js deleted file mode 100644 index 4c1d68d109a..00000000000 --- a/web-client/src/presenter/actions/setFullUserOnFormAction.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import { presenter } from '../presenter-mock'; -import { runAction } from 'cerebral/test'; -import { setFullUserOnFormAction } from './setFullUserOnFormAction'; - -describe('setFullUserOnFormAction', () => { - it('sets all user fields from props.user on state.form', async () => { - const mockUser = { - barNumber: 'TU1234', - contact: { - address1: '123 Main St', - }, - email: 'test@example.com', - name: 'Test User', - userId: '123', - }; - - const result = await runAction(setFullUserOnFormAction, { - modules: { - presenter, - }, - props: { - user: mockUser, - }, - state: {}, - }); - expect(result.state.form).toEqual(mockUser); - }); -}); diff --git a/web-client/src/presenter/actions/setMessageAction.js b/web-client/src/presenter/actions/setMessageAction.js new file mode 100644 index 00000000000..75fd48afff4 --- /dev/null +++ b/web-client/src/presenter/actions/setMessageAction.js @@ -0,0 +1,12 @@ +import { state } from 'cerebral'; + +/** + * sets the state.messageDetail from props.messageDetail + * + * @param {object} providers the providers object + * @param {object} providers.props the cerebral props object containing the props.messageDetail + * @param {object} providers.store the cerebral store used for setting the state.messageDetail + */ +export const setMessageAction = ({ props, store }) => { + store.set(state.messageDetail, props.messageDetail); +}; diff --git a/web-client/src/presenter/actions/setMessageAction.test.js b/web-client/src/presenter/actions/setMessageAction.test.js new file mode 100644 index 00000000000..0b9de0ae953 --- /dev/null +++ b/web-client/src/presenter/actions/setMessageAction.test.js @@ -0,0 +1,18 @@ +import { runAction } from 'cerebral/test'; +import { setMessageAction } from './setMessageAction'; + +describe('setMessageAction', () => { + it('sets state.caseDetail from props', async () => { + const { state } = await runAction(setMessageAction, { + props: { + messageDetail: { + messageId: '4b194c7b-6305-4d46-b8dd-6962066574f7', + }, + }, + }); + + expect(state.messageDetail).toEqual({ + messageId: '4b194c7b-6305-4d46-b8dd-6962066574f7', + }); + }); +}); diff --git a/web-client/src/presenter/actions/setModalTitleAction.js b/web-client/src/presenter/actions/setModalTitleAction.js new file mode 100644 index 00000000000..c51322af5b2 --- /dev/null +++ b/web-client/src/presenter/actions/setModalTitleAction.js @@ -0,0 +1,13 @@ +import { state } from 'cerebral'; + +/** + * sets the modal title + * + * @param {object} providers the providers object + * @param {object} providers.props the cerebral props object + * @param {object} providers.store the cerebral store object + * @returns {void} + */ +export const setModalTitleAction = ({ props, store }) => { + store.set(state.modal.title, props.title); +}; diff --git a/web-client/src/presenter/actions/setRequestAccessWizardStepActionGenerator.js b/web-client/src/presenter/actions/setRequestAccessWizardStepActionGenerator.js new file mode 100644 index 00000000000..d7cc4ffb0ab --- /dev/null +++ b/web-client/src/presenter/actions/setRequestAccessWizardStepActionGenerator.js @@ -0,0 +1,13 @@ +import { state } from 'cerebral'; + +/** + * Sets the state.wizardStep to the value passed in. + * + * @param {string} wizardStep the wizardStep + * @returns {Function} a function that sets the wizardStep + */ +export const setRequestAccessWizardStepActionGenerator = wizardStep => { + return ({ store }) => { + store.set(state.wizardStep, wizardStep); + }; +}; diff --git a/web-client/src/presenter/actions/setRequestAccessWizardStepActionGenerator.test.js b/web-client/src/presenter/actions/setRequestAccessWizardStepActionGenerator.test.js new file mode 100644 index 00000000000..6496dc0a285 --- /dev/null +++ b/web-client/src/presenter/actions/setRequestAccessWizardStepActionGenerator.test.js @@ -0,0 +1,13 @@ +import { runAction } from 'cerebral/test'; +import { setRequestAccessWizardStepActionGenerator } from './setRequestAccessWizardStepActionGenerator'; + +describe('setRequestAccessWizardStepActionGenerator', () => { + it('should set state.wizardStep to the value passed in', async () => { + const mockWizardStep = 'TestWizardStep'; + const { state } = await runAction( + setRequestAccessWizardStepActionGenerator(mockWizardStep), + ); + + expect(state.wizardStep).toBe(mockWizardStep); + }); +}); diff --git a/web-client/src/presenter/actions/setSectionInboxCountAction.test.js b/web-client/src/presenter/actions/setSectionInboxCountAction.test.js index 71cdbfc734c..5393ad7ec93 100644 --- a/web-client/src/presenter/actions/setSectionInboxCountAction.test.js +++ b/web-client/src/presenter/actions/setSectionInboxCountAction.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -51,7 +51,7 @@ describe('setSectionInboxCountAction', () => { it('sets sectionInboxCount for a docketClerk user', async () => { applicationContext.getCurrentUser.mockReturnValue({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }); const result = await runAction(setSectionInboxCountAction, { @@ -74,7 +74,7 @@ describe('setSectionInboxCountAction', () => { it('sets sectionInboxCount for a judge user', async () => { applicationContext.getCurrentUser.mockReturnValue({ name: 'Judge Barker', - role: User.ROLES.judge, + role: ROLES.judge, }); const result = await runAction(setSectionInboxCountAction, { @@ -99,7 +99,7 @@ describe('setSectionInboxCountAction', () => { it('sets sectionInboxCount for a chambers user', async () => { applicationContext.getCurrentUser.mockReturnValue({ name: 'ADC', - role: User.ROLES.adc, + role: ROLES.adc, }); const result = await runAction(setSectionInboxCountAction, { diff --git a/web-client/src/presenter/actions/setStatisticIndexAction.js b/web-client/src/presenter/actions/setStatisticIndexAction.js index e0b52ed2203..5ed51d522eb 100644 --- a/web-client/src/presenter/actions/setStatisticIndexAction.js +++ b/web-client/src/presenter/actions/setStatisticIndexAction.js @@ -10,4 +10,5 @@ import { state } from 'cerebral'; */ export const setStatisticIndexAction = ({ props, store }) => { store.set(state.modal.statisticIndex, props.statisticIndex); + store.set(state.modal.key, props.key); }; diff --git a/web-client/src/presenter/actions/setStatisticIndexAction.test.js b/web-client/src/presenter/actions/setStatisticIndexAction.test.js index 9a7ebd27cce..f995c04f87b 100644 --- a/web-client/src/presenter/actions/setStatisticIndexAction.test.js +++ b/web-client/src/presenter/actions/setStatisticIndexAction.test.js @@ -5,6 +5,7 @@ describe('setStatisticIndexAction,', () => { it('sets the statisticsIndex from props', async () => { const result = await runAction(setStatisticIndexAction, { props: { + key: 'totalPenalties', statisticIndex: 5, }, state: { @@ -15,5 +16,6 @@ describe('setStatisticIndexAction,', () => { }); expect(result.state.modal.statisticIndex).toEqual(5); + expect(result.state.modal.key).toEqual('totalPenalties'); }); }); diff --git a/web-client/src/presenter/actions/setTotalPenaltiesAmountForAddStatisticAction.js b/web-client/src/presenter/actions/setTotalPenaltiesAmountForAddStatisticAction.js new file mode 100644 index 00000000000..d1831042334 --- /dev/null +++ b/web-client/src/presenter/actions/setTotalPenaltiesAmountForAddStatisticAction.js @@ -0,0 +1,19 @@ +import { state } from 'cerebral'; + +/** + * Sets the total penalties amount from props on the statistic from the given index from props + * + * @param {object} providers the providers object + * @param {object} providers.props the cerebral props object + * @param {object} providers.store there cerebral store object + * @returns {void} sets the form value for the given props.statisticIndex + */ +export const setTotalPenaltiesAmountForAddStatisticAction = ({ + get, + props, + store, +}) => { + const { totalPenalties } = props; + const { key } = get(state.modal); + store.set(state.form[key], totalPenalties); +}; diff --git a/web-client/src/presenter/actions/setTotalPenaltiesAmountForAddStatisticAction.test.js b/web-client/src/presenter/actions/setTotalPenaltiesAmountForAddStatisticAction.test.js new file mode 100644 index 00000000000..2d1f80fad18 --- /dev/null +++ b/web-client/src/presenter/actions/setTotalPenaltiesAmountForAddStatisticAction.test.js @@ -0,0 +1,23 @@ +import { runAction } from 'cerebral/test'; +import { setTotalPenaltiesAmountForAddStatisticAction } from './setTotalPenaltiesAmountForAddStatisticAction'; + +describe('setTotalPenaltiesAmountForAddStatisticAction,', () => { + it('sets the totalPenalties value for the given index', async () => { + const result = await runAction( + setTotalPenaltiesAmountForAddStatisticAction, + { + props: { + totalPenalties: '$112.99', + }, + state: { + form: {}, + modal: { + key: 'totalPenalties', + }, + }, + }, + ); + + expect(result.state.form.totalPenalties).toEqual('$112.99'); + }); +}); diff --git a/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.js b/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.js index 91d3380146b..bb271e86a47 100644 --- a/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.js +++ b/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.js @@ -18,7 +18,7 @@ export const setTotalPenaltiesAmountForStatisticAction = ({ const { statisticIndex } = get(state.modal); store.set( - state.form.statistics[statisticIndex].totalPenalties, + state.form.statistics[statisticIndex].irsTotalPenalties, totalPenalties, ); }; diff --git a/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.test.js b/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.test.js index 92d23eb4770..d66bbe7d791 100644 --- a/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.test.js +++ b/web-client/src/presenter/actions/setTotalPenaltiesAmountForStatisticAction.test.js @@ -11,13 +11,13 @@ describe('setTotalPenaltiesAmountForStatisticAction,', () => { form: { statistics: [ { - totalPenalties: '$1.00', + irsTotalPenalties: '$1.00', }, { - totalPenalties: '$2.00', + irsTotalPenalties: '$2.00', }, { - totalPenalties: '$3.00', + irsTotalPenalties: '$3.00', }, ], }, @@ -29,13 +29,13 @@ describe('setTotalPenaltiesAmountForStatisticAction,', () => { expect(result.state.form.statistics).toMatchObject([ { - totalPenalties: '$1.00', + irsTotalPenalties: '$1.00', }, { - totalPenalties: '$112.99', + irsTotalPenalties: '$112.99', }, { - totalPenalties: '$3.00', + irsTotalPenalties: '$3.00', }, ]); }); diff --git a/web-client/src/presenter/actions/setUserPermissionsAction.test.js b/web-client/src/presenter/actions/setUserPermissionsAction.test.js index 7dde70c21ff..4829fcce800 100644 --- a/web-client/src/presenter/actions/setUserPermissionsAction.test.js +++ b/web-client/src/presenter/actions/setUserPermissionsAction.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; @@ -7,7 +7,7 @@ import { runAction } from 'cerebral/test'; import { setUserPermissionsAction } from './setUserPermissionsAction'; describe('setUserPermissionsAction', () => { - const mockUser = { role: User.ROLES.docketClerk }; + const mockUser = { role: ROLES.docketClerk }; beforeAll(() => { presenter.providers.applicationContext = applicationContext; diff --git a/web-client/src/presenter/actions/setupEditPetitionDetailFormAction.test.js b/web-client/src/presenter/actions/setupEditPetitionDetailFormAction.test.js index ef1e540f544..481861b9cb8 100644 --- a/web-client/src/presenter/actions/setupEditPetitionDetailFormAction.test.js +++ b/web-client/src/presenter/actions/setupEditPetitionDetailFormAction.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; +import { PAYMENT_STATUS } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -17,7 +17,7 @@ describe('setupEditPetitionDetailFormAction', () => { state: { caseDetail: { caseType: 'some case type', - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, petitionPaymentWaivedDate: '2019-03-01T21:40:46.415Z', preferredTrialCity: 'Fresno, California', procedureType: 'Small', @@ -31,7 +31,7 @@ describe('setupEditPetitionDetailFormAction', () => { paymentDateWaivedDay: '01', paymentDateWaivedMonth: '03', paymentDateWaivedYear: '2019', - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, preferredTrialCity: 'Fresno, California', procedureType: 'Small', }); @@ -45,7 +45,7 @@ describe('setupEditPetitionDetailFormAction', () => { state: { caseDetail: { petitionPaymentDate: '2019-03-01T21:40:46.415Z', - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, + petitionPaymentStatus: PAYMENT_STATUS.PAID, }, form: {}, }, @@ -55,7 +55,7 @@ describe('setupEditPetitionDetailFormAction', () => { paymentDateDay: '01', paymentDateMonth: '03', paymentDateYear: '2019', - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, + petitionPaymentStatus: PAYMENT_STATUS.PAID, }); }); @@ -66,14 +66,14 @@ describe('setupEditPetitionDetailFormAction', () => { }, state: { caseDetail: { - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, }, form: {}, }, }); expect(result.state.form).toEqual({ - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, }); }); diff --git a/web-client/src/presenter/actions/startScanAction.test.js b/web-client/src/presenter/actions/startScanAction.test.js index 8f904c7f747..133c1d21850 100644 --- a/web-client/src/presenter/actions/startScanAction.test.js +++ b/web-client/src/presenter/actions/startScanAction.test.js @@ -1,4 +1,4 @@ -import { Scan } from '../../../../shared/src/business/entities/Scan'; +import { SCAN_MODES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { presenter } from '../presenter-mock'; import { runAction } from 'cerebral/test'; @@ -22,7 +22,7 @@ describe('startScanAction', () => { presenter, }, props: { - scanMode: Scan.SCAN_MODES.FEEDER, + scanMode: SCAN_MODES.FEEDER, scannerSourceIndex: 0, scannerSourceName: 'scanner', }, @@ -46,7 +46,7 @@ describe('startScanAction', () => { presenter, }, props: { - scanMode: Scan.SCAN_MODES.FEEDER, + scanMode: SCAN_MODES.FEEDER, scannerSourceIndex: 0, scannerSourceName: 'scanner', }, @@ -99,7 +99,7 @@ describe('startScanAction', () => { presenter, }, props: { - scanMode: Scan.SCAN_MODES.FEEDER, + scanMode: SCAN_MODES.FEEDER, scannerSourceIndex: 0, scannerSourceName: 'scanner', }, diff --git a/web-client/src/presenter/actions/submitAddDeficiencyStatisticsAction.js b/web-client/src/presenter/actions/submitAddDeficiencyStatisticsAction.js new file mode 100644 index 00000000000..18f8f48c131 --- /dev/null +++ b/web-client/src/presenter/actions/submitAddDeficiencyStatisticsAction.js @@ -0,0 +1,57 @@ +import { combineLastDateOfPeriodFields } from './StartCaseInternal/computeStatisticDatesAction'; +import { state } from 'cerebral'; +/** + * submits the add deficiency statistics form + * + * @param {object} providers the providers object + * @param {object} providers.get the cerebral get function + * @param {object} providers.applicationContext the applicationContext + * @param {object} providers.path the next object in the path + * @returns {Promise<*>} the success or error path + */ +export const submitAddDeficiencyStatisticsAction = async ({ + applicationContext, + get, + path, +}) => { + const { + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + year, + yearOrPeriod, + } = combineLastDateOfPeriodFields({ + applicationContext, + form: get(state.form), + }); + + const { caseId } = get(state.caseDetail); + + try { + await applicationContext.getUseCases().addDeficiencyStatisticInteractor({ + applicationContext, + caseId, + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + year, + yearOrPeriod, + }); + + return path.success({ + alertSuccess: { + message: 'Year/Period added.', + }, + }); + } catch (e) { + return path.error({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + } +}; diff --git a/web-client/src/presenter/actions/submitAddDeficiencyStatisticsAction.test.js b/web-client/src/presenter/actions/submitAddDeficiencyStatisticsAction.test.js new file mode 100644 index 00000000000..7039fc70031 --- /dev/null +++ b/web-client/src/presenter/actions/submitAddDeficiencyStatisticsAction.test.js @@ -0,0 +1,76 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { submitAddDeficiencyStatisticsAction } from './submitAddDeficiencyStatisticsAction'; + +presenter.providers.applicationContext = applicationContext; +presenter.providers.path = { + error: jest.fn(), + success: jest.fn(), +}; + +describe('submitAddDeficiencyStatisticsAction', () => { + it('calls the deficiency statistics submit interactor, returning the success path', async () => { + await runAction(submitAddDeficiencyStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + }, + form: { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriod: null, + year: '2019', + yearOrPeriod: 'Year', + }, + }, + }); + + expect( + applicationContext.getUseCases().addDeficiencyStatisticInteractor, + ).toHaveBeenCalled(); + expect(presenter.providers.path.success).toHaveBeenCalledWith({ + alertSuccess: { message: 'Year/Period added.' }, + }); + }); + + it('returns the error path if an error is encountered when calling the interactor', async () => { + presenter.providers.applicationContext + .getUseCases() + .addDeficiencyStatisticInteractor.mockImplementationOnce(() => { + throw new Error('error'); + }); + + await runAction(submitAddDeficiencyStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + }, + form: { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriod: null, + year: '2019', + yearOrPeriod: 'Year', + }, + }, + }); + + expect(presenter.providers.path.success).not.toHaveBeenCalled(); + expect(presenter.providers.path.error).toHaveBeenCalledWith({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + }); +}); diff --git a/web-client/src/presenter/actions/submitEditDeficiencyStatisticAction.js b/web-client/src/presenter/actions/submitEditDeficiencyStatisticAction.js new file mode 100644 index 00000000000..75fe3d6272a --- /dev/null +++ b/web-client/src/presenter/actions/submitEditDeficiencyStatisticAction.js @@ -0,0 +1,69 @@ +import { combineLastDateOfPeriodFields } from './StartCaseInternal/computeStatisticDatesAction'; +import { state } from 'cerebral'; +/** + * submits the edit deficiency statistics form + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the applicationContext + * @param {object} providers.get the cerebral get function + * @param {object} providers.path the next object in the path + * @returns {Promise<*>} the success or error path + */ +export const submitEditDeficiencyStatisticAction = async ({ + applicationContext, + get, + path, +}) => { + const { + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + statisticId, + year, + yearOrPeriod, + } = combineLastDateOfPeriodFields({ + applicationContext, + form: get(state.form), + }); + + const { caseId } = get(state.caseDetail); + + try { + await applicationContext.getUseCases().updateDeficiencyStatisticInteractor({ + applicationContext, + caseId, + determinationDeficiencyAmount, + determinationTotalPenalties, + irsDeficiencyAmount, + irsTotalPenalties, + lastDateOfPeriod, + statisticId, + year, + yearOrPeriod, + }); + + let successMessageDate; + + if (yearOrPeriod === 'Year') { + successMessageDate = year; + } else { + successMessageDate = applicationContext + .getUtilities() + .formatDateString(lastDateOfPeriod, 'MMDDYY'); + } + + return path.success({ + alertSuccess: { + message: `${successMessageDate} statistics updated.`, + }, + }); + } catch (e) { + return path.error({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + } +}; diff --git a/web-client/src/presenter/actions/submitEditDeficiencyStatisticAction.test.js b/web-client/src/presenter/actions/submitEditDeficiencyStatisticAction.test.js new file mode 100644 index 00000000000..853b613d151 --- /dev/null +++ b/web-client/src/presenter/actions/submitEditDeficiencyStatisticAction.test.js @@ -0,0 +1,116 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { submitEditDeficiencyStatisticAction } from './submitEditDeficiencyStatisticAction'; + +presenter.providers.applicationContext = applicationContext; +presenter.providers.path = { + error: jest.fn(), + success: jest.fn(), +}; + +describe('submitEditDeficiencyStatisticAction', () => { + it('calls the deficiency statistics update interactor, returning the success path', async () => { + const statistic = { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriod: null, + year: '2019', + yearOrPeriod: 'Year', + }; + + await runAction(submitEditDeficiencyStatisticAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + statistics: [statistic], + }, + form: { ...statistic, statisticIndex: 0, year: 2012 }, + }, + }); + + expect( + applicationContext.getUseCases().updateDeficiencyStatisticInteractor, + ).toHaveBeenCalled(); + expect(presenter.providers.path.success).toHaveBeenCalledWith({ + alertSuccess: { message: '2012 statistics updated.' }, + }); + }); + + it('calls the deficiency statistics update interactor, returning the success path with a lastDateOfPeriod success message', async () => { + const statistic = { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriodDay: '01', + lastDateOfPeriodMonth: '03', + lastDateOfPeriodYear: '2019', + year: null, + yearOrPeriod: 'Period', + }; + + await runAction(submitEditDeficiencyStatisticAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + statistics: [statistic], + }, + form: { ...statistic, irsTotalPenalties: 5, statisticIndex: 0 }, + }, + }); + + expect( + applicationContext.getUseCases().updateDeficiencyStatisticInteractor, + ).toHaveBeenCalled(); + expect(presenter.providers.path.success).toHaveBeenCalledWith({ + alertSuccess: { message: '03/01/19 statistics updated.' }, + }); + }); + + it('returns the error path if an error is encountered when calling the interactor', async () => { + presenter.providers.applicationContext + .getUseCases() + .updateDeficiencyStatisticInteractor.mockImplementationOnce(() => { + throw new Error('error'); + }); + + const statistic = { + determinationDeficiencyAmount: '1', + determinationTotalPenalties: '2', + irsDeficiencyAmount: '3', + irsTotalPenalties: '4', + lastDateOfPeriod: null, + year: '2019', + yearOrPeriod: 'Year', + }; + + await runAction(submitEditDeficiencyStatisticAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + statistics: [statistic], + }, + form: { ...statistic, statisticIndex: 0 }, + }, + }); + + expect(presenter.providers.path.success).not.toHaveBeenCalled(); + expect(presenter.providers.path.error).toHaveBeenCalledWith({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + }); +}); diff --git a/web-client/src/presenter/actions/submitOtherStatisticsAction.js b/web-client/src/presenter/actions/submitOtherStatisticsAction.js new file mode 100644 index 00000000000..a68040f23c0 --- /dev/null +++ b/web-client/src/presenter/actions/submitOtherStatisticsAction.js @@ -0,0 +1,44 @@ +import { state } from 'cerebral'; + +/** + * submits the add/edit other statistics form + * + * @param {object} providers the providers object + * @param {object} providers.get the cerebral get function + * @param {object} providers.applicationContext the applicationContext + * @param {object} providers.path the next object in the path + * @returns {Promise<*>} the success or error path + */ +export const submitOtherStatisticsAction = async ({ + applicationContext, + get, + path, +}) => { + const { damages, isEditing, litigationCosts } = get(state.form); + const { caseId } = get(state.caseDetail); + + try { + await applicationContext.getUseCases().updateOtherStatisticsInteractor({ + applicationContext, + caseId, + damages, + litigationCosts, + }); + + let successMessage = 'Other statistics added.'; + if (isEditing) { + successMessage = 'Other statistics updated.'; + } + return path.success({ + alertSuccess: { + message: successMessage, + }, + }); + } catch (e) { + return path.error({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + } +}; diff --git a/web-client/src/presenter/actions/submitOtherStatisticsAction.test.js b/web-client/src/presenter/actions/submitOtherStatisticsAction.test.js new file mode 100644 index 00000000000..29e703e9aa2 --- /dev/null +++ b/web-client/src/presenter/actions/submitOtherStatisticsAction.test.js @@ -0,0 +1,91 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { submitOtherStatisticsAction } from './submitOtherStatisticsAction'; + +presenter.providers.applicationContext = applicationContext; +presenter.providers.path = { + error: jest.fn(), + success: jest.fn(), +}; + +describe('submitOtherStatisticsAction', () => { + it('calls the other statistics submit interactor, returning the success path', async () => { + await runAction(submitOtherStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + }, + form: { + damages: '1', + litigationCosts: '5', + }, + }, + }); + + expect( + applicationContext.getUseCases().updateOtherStatisticsInteractor, + ).toHaveBeenCalled(); + expect(presenter.providers.path.success).toHaveBeenCalledWith({ + alertSuccess: { message: 'Other statistics added.' }, + }); + }); + + it('calls the other statistics submit interactor, returning the success path with the isEditing success message', async () => { + await runAction(submitOtherStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + }, + form: { + damages: '1', + isEditing: true, + litigationCosts: '5', + }, + }, + }); + + expect( + applicationContext.getUseCases().updateOtherStatisticsInteractor, + ).toHaveBeenCalled(); + expect(presenter.providers.path.success).toHaveBeenCalledWith({ + alertSuccess: { message: 'Other statistics updated.' }, + }); + }); + + it('returns the error path if an error is encountered when calling the interactor', async () => { + presenter.providers.applicationContext + .getUseCases() + .updateOtherStatisticsInteractor.mockImplementationOnce(() => { + throw new Error('error'); + }); + + await runAction(submitOtherStatisticsAction, { + modules: { + presenter, + }, + state: { + caseDetail: { + caseId: '123', + }, + form: { + damages: '1', + litigationCosts: '5', + }, + }, + }); + + expect(presenter.providers.path.success).not.toHaveBeenCalled(); + expect(presenter.providers.path.error).toHaveBeenCalledWith({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + }); + }); +}); diff --git a/web-client/src/presenter/actions/unsetRequestAccessWizardStepAction.js b/web-client/src/presenter/actions/unsetRequestAccessWizardStepAction.js new file mode 100644 index 00000000000..dda47ad13a2 --- /dev/null +++ b/web-client/src/presenter/actions/unsetRequestAccessWizardStepAction.js @@ -0,0 +1,11 @@ +import { state } from 'cerebral'; + +/** + * Clears the state.wizardStep. + * + * @param {object} providers the providers object + * @param {object} providers.store the cerebral store + */ +export const unsetRequestAccessWizardStepAction = ({ store }) => { + store.unset(state.wizardStep); +}; diff --git a/web-client/src/presenter/actions/unsetRequestAccessWizardStepAction.test.js b/web-client/src/presenter/actions/unsetRequestAccessWizardStepAction.test.js new file mode 100644 index 00000000000..a4562f9e521 --- /dev/null +++ b/web-client/src/presenter/actions/unsetRequestAccessWizardStepAction.test.js @@ -0,0 +1,14 @@ +import { runAction } from 'cerebral/test'; +import { unsetRequestAccessWizardStepAction } from './unsetRequestAccessWizardStepAction'; + +describe('unsetRequestAccessWizardStepAction', () => { + it('should clear wizardStep from state', async () => { + const { state } = await runAction(unsetRequestAccessWizardStepAction, { + state: { + wizardStep: 'TestWizardStep', + }, + }); + + expect(state.wizardStep).toBeUndefined(); + }); +}); diff --git a/web-client/src/presenter/actions/updateCreateCaseMessageAttachmentsAction.js b/web-client/src/presenter/actions/updateCreateCaseMessageAttachmentsAction.js new file mode 100644 index 00000000000..c294d6494d3 --- /dev/null +++ b/web-client/src/presenter/actions/updateCreateCaseMessageAttachmentsAction.js @@ -0,0 +1,35 @@ +import { state } from 'cerebral'; + +/** + * updates the current case message form data's attachments field + * + * @param {object} providers the providers object + * @param {object} providers.get the get function to retrieve values from state + * @param {object} providers.props the cerebral props object + * @param {object} providers.store the cerebral store object + */ +export const updateCreateCaseMessageAttachmentsAction = ({ + get, + props, + store, +}) => { + const { attachments } = get(state.modal.form); + const { documents } = get(state.caseDetail); + const { documentId } = props; + + // TODO: Should we evaluate the length of the array and conditionally push? + if (documentId) { + const document = documents.find( + document => document.documentId === documentId, + ); + + const documentTitle = document.documentTitle || document.documentType; + + attachments.push({ + documentId, + documentTitle, + }); + + store.set(state.modal.form.attachments, attachments); + } +}; diff --git a/web-client/src/presenter/actions/updateCreateCaseMessageAttachmentsAction.test.js b/web-client/src/presenter/actions/updateCreateCaseMessageAttachmentsAction.test.js new file mode 100644 index 00000000000..dbe07e0ecfc --- /dev/null +++ b/web-client/src/presenter/actions/updateCreateCaseMessageAttachmentsAction.test.js @@ -0,0 +1,52 @@ +import { runAction } from 'cerebral/test'; +import { updateCreateCaseMessageAttachmentsAction } from './updateCreateCaseMessageAttachmentsAction'; + +describe('updateCreateCaseMessageAttachmentsAction', () => { + const caseDetail = { + documents: [ + { + documentId: '123', + documentType: 'Petition', + }, + ], + }; + + it('appends the given document meta from props to the form.modal.attachments array', async () => { + const result = await runAction(updateCreateCaseMessageAttachmentsAction, { + props: { + documentId: '123', + }, + state: { + caseDetail, + modal: { + form: { + attachments: [], + }, + }, + }, + }); + + expect(result.state.modal.form.attachments).toEqual([ + { documentId: '123', documentTitle: 'Petition' }, + ]); + }); + + it('does not modify the array if no documentId is given', async () => { + const result = await runAction(updateCreateCaseMessageAttachmentsAction, { + props: { + documentId: '', + documentTitle: '', + }, + state: { + caseDetail, + modal: { + form: { + attachments: [], + }, + }, + }, + }); + + expect(result.state.modal.form.attachments).toEqual([]); + }); +}); diff --git a/web-client/src/presenter/actions/updatePractitionerUserAction.js b/web-client/src/presenter/actions/updatePractitionerUserAction.js index a62a066a35c..c6cd6b359b1 100644 --- a/web-client/src/presenter/actions/updatePractitionerUserAction.js +++ b/web-client/src/presenter/actions/updatePractitionerUserAction.js @@ -7,6 +7,7 @@ import { state } from 'cerebral'; * @param {object} providers.applicationContext the applicationContext * @param {Function} providers.get the cerebral get function * @param {object} providers.path the next object in the path + * @returns {object} path execution results */ export const updatePractitionerUserAction = async ({ applicationContext, diff --git a/web-client/src/presenter/actions/validateAddDeficiencyStatisticsAction.js b/web-client/src/presenter/actions/validateAddDeficiencyStatisticsAction.js new file mode 100644 index 00000000000..49fcd07190e --- /dev/null +++ b/web-client/src/presenter/actions/validateAddDeficiencyStatisticsAction.js @@ -0,0 +1,44 @@ +import { combineLastDateOfPeriodFields } from './StartCaseInternal/computeStatisticDatesAction'; +import { state } from 'cerebral'; + +/** + * validates the add deficiency statistic. + * + * @param {object} providers the providers object + * @param {object} providers.applicationContext the application context needed for getting the validatePetition use case + * @param {object} providers.get the cerebral get function used for getting state.form + * @param {object} providers.path the cerebral path which contains the next path in the sequence (path of success or error) + * @returns {object} the next path based on if validation was successful or error + */ +export const validateAddDeficiencyStatisticsAction = ({ + applicationContext, + get, + path, +}) => { + const form = get(state.form); + + const combinedForm = combineLastDateOfPeriodFields({ + applicationContext, + form, + }); + + const errors = applicationContext + .getUseCases() + .validateAddDeficiencyStatisticsInteractor({ + applicationContext, + statistic: { + ...combinedForm, + }, + }); + + if (!errors) { + return path.success(); + } else { + return path.error({ + alertError: { + title: 'Errors were found. Please correct your form and resubmit.', + }, + errors, + }); + } +}; diff --git a/web-client/src/presenter/actions/validateCaseDetailAction.js b/web-client/src/presenter/actions/validateCaseDetailAction.js index 0042b0a50cb..d88a3b64408 100644 --- a/web-client/src/presenter/actions/validateCaseDetailAction.js +++ b/web-client/src/presenter/actions/validateCaseDetailAction.js @@ -1,17 +1,20 @@ +import { aggregateStatisticsErrors } from './validatePetitionFromPaperAction'; import { state } from 'cerebral'; /** * validates the case detail form and sets state.validationErrors when errors occur. * * @param {object} providers the providers object - * @param {object} providers.store the cerebral store used for setting the state.validationErrors when validation errors occur * @param {object} providers.applicationContext the application context needed for getting the getUseCaseForDocumentUpload use case + * @param {Function} providers.get the cerebral get function * @param {object} providers.path the cerebral path which contains the next path in the sequence (path of success or failure) * @param {object} providers.props the cerebral store used for getting the props.formWithComputedDates + * @param {object} providers.store the cerebral store used for setting the state.validationErrors when validation errors occur * @returns {object} the alertSuccess and the generated docketNumber */ export const validateCaseDetailAction = ({ applicationContext, + get, path, props, store, @@ -30,6 +33,15 @@ export const validateCaseDetailAction = ({ formWithComputedDates, }); } else { - return path.error({ errors }); + const errorDisplayMap = { + statistics: 'Statistics', + }; + + const statisticsErrors = aggregateStatisticsErrors({ errors, get }); + if (statisticsErrors) { + errors.statistics = statisticsErrors; + } + + return path.error({ errorDisplayMap, errors }); } }; diff --git a/web-client/src/presenter/actions/validateCaseDetailAction.test.js b/web-client/src/presenter/actions/validateCaseDetailAction.test.js index 6f49c14565b..33333bf772d 100644 --- a/web-client/src/presenter/actions/validateCaseDetailAction.test.js +++ b/web-client/src/presenter/actions/validateCaseDetailAction.test.js @@ -21,6 +21,7 @@ describe('validateCaseDetail', () => { .getUseCases() .validateCaseDetailInteractor.mockReturnValue(null); }); + it('should call the success path when no errors are found', async () => { await runAction(validateCaseDetailAction, { modules: { @@ -66,4 +67,42 @@ describe('validateCaseDetail', () => { }); expect(errorStub.mock.calls.length).toEqual(1); }); + + it('aggregates statistics errors if present', async () => { + applicationContext + .getUseCases() + .validateCaseDetailInteractor.mockReturnValue({ + statistics: [ + { deficiency: 'enter deficiency amount', index: 1 }, + { index: 2, irsTotalPenalties: 'enter total penalties' }, + ], + }); + + await runAction(validateCaseDetailAction, { + modules: { + presenter, + }, + state: { + form: { + statistics: [ + { yearOrPeriod: 'Year' }, + { yearOrPeriod: 'Period' }, + { yearOrPeriod: 'Year' }, + ], + }, + }, + }); + + expect(errorStub.mock.calls[0][0].errors.statistics).toEqual([ + {}, + { + enterAllValues: 'Enter period, deficiency amount, and total penalties', + index: 1, + }, + { + enterAllValues: 'Enter year, deficiency amount, and total penalties', + index: 2, + }, + ]); + }); }); diff --git a/web-client/src/presenter/actions/validateCreateCaseMessageAction.js b/web-client/src/presenter/actions/validateCreateCaseMessageAction.js new file mode 100644 index 00000000000..9aa05ae4815 --- /dev/null +++ b/web-client/src/presenter/actions/validateCreateCaseMessageAction.js @@ -0,0 +1,31 @@ +import { state } from 'cerebral'; + +/** + * validates the create case message modal + * + * @param {object} providers the providers object + * @param {object} providers.get the cerebral get function + * @param {object} providers.path the path to take if validation fails or not + * @param {object} providers.applicationContext the application context needed for getting the validateForwardMessage use case + * @returns {object} path.success or path.error + */ +export const validateCreateCaseMessageAction = ({ + applicationContext, + get, + path, +}) => { + const message = get(state.modal.form); + + const errors = applicationContext + .getUseCases() + .validateCreateCaseMessageInteractor({ + applicationContext, + message, + }); + + if (!errors) { + return path.success(); + } else { + return path.error({ errors }); + } +}; diff --git a/web-client/src/presenter/actions/validateCreateCaseMessageAction.test.js b/web-client/src/presenter/actions/validateCreateCaseMessageAction.test.js new file mode 100644 index 00000000000..8f7b517ef4b --- /dev/null +++ b/web-client/src/presenter/actions/validateCreateCaseMessageAction.test.js @@ -0,0 +1,90 @@ +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { presenter } from '../presenter-mock'; +import { runAction } from 'cerebral/test'; +import { validateCreateCaseMessageAction } from './validateCreateCaseMessageAction'; + +describe('validateCreateCaseMessageAction', () => { + let successStub; + let errorStub; + + beforeAll(() => { + successStub = jest.fn(); + errorStub = jest.fn(); + + presenter.providers.applicationContext = applicationContext; + presenter.providers.path = { + error: errorStub, + success: successStub, + }; + + applicationContext + .getUseCases() + .validateCreateCaseMessageInteractor.mockReturnValue(null); + }); + + it('should call the success path when no errors are found', async () => { + await runAction(validateCreateCaseMessageAction, { + modules: { + presenter, + }, + props: {}, + state: { + modal: { + form: { + caseId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + from: 'yup', + fromSection: 'yup', + fromUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + message: 'yup', + subject: 'hi', + to: 'yup', + toSection: 'yup', + toUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + }, + }, + }, + }); + expect( + applicationContext.getUseCases().validateCreateCaseMessageInteractor.mock + .calls[0][0].message, + ).toMatchObject({ + caseId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + from: 'yup', + fromSection: 'yup', + fromUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + message: 'yup', + subject: 'hi', + to: 'yup', + toSection: 'yup', + toUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + }); + expect(successStub.mock.calls.length).toEqual(1); + }); + + it('should call the error path when any errors are found', async () => { + applicationContext + .getUseCases() + .validateCreateCaseMessageInteractor.mockReturnValue('error'); + + await runAction(validateCreateCaseMessageAction, { + modules: { + presenter, + }, + state: { + modal: { + form: { + caseId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + from: 'yup', + fromSection: 'yup', + fromUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + message: 'yup', + to: 'yup', + toSection: 'yup', + toUserId: 'fa1179bd-04f5-4934-a716-964d8d7babc6', + }, + }, + }, + }); + expect(errorStub.mock.calls.length).toEqual(1); + }); +}); diff --git a/web-client/src/presenter/actions/validatePetitionFromPaperAction.js b/web-client/src/presenter/actions/validatePetitionFromPaperAction.js index 5836aae55e3..a0eeeaeaeb8 100644 --- a/web-client/src/presenter/actions/validatePetitionFromPaperAction.js +++ b/web-client/src/presenter/actions/validatePetitionFromPaperAction.js @@ -1,12 +1,46 @@ import { state } from 'cerebral'; +export const aggregateStatisticsErrors = ({ errors, get }) => { + let newErrorStatistics; + + if (errors.statistics) { + newErrorStatistics = []; + const formStatistics = get(state.form.statistics); + + formStatistics.forEach((formStatistic, index) => { + const errorStatistic = errors.statistics.find(s => s.index === index); + if (errorStatistic) { + if (formStatistic.yearOrPeriod === 'Year') { + newErrorStatistics.push({ + enterAllValues: + 'Enter year, deficiency amount, and total penalties', + index, + }); + } else { + newErrorStatistics.push({ + enterAllValues: + 'Enter period, deficiency amount, and total penalties', + index, + }); + } + } else { + newErrorStatistics.push({}); + } + }); + + errors.statistics = newErrorStatistics; + } + + return newErrorStatistics; +}; + /** * validates the petition. * * @param {object} providers the providers object * @param {object} providers.applicationContext the application context needed for getting the validatePetition use case - * @param {object} providers.path the cerebral path which contains the next path in the sequence (path of success or error) * @param {object} providers.get the cerebral get function used for getting state.form + * @param {object} providers.path the cerebral path which contains the next path in the sequence (path of success or error) * @param {object} providers.props the cerebral props object * @returns {object} the next path based on if validation was successful or error */ @@ -39,32 +73,9 @@ export const validatePetitionFromPaperAction = ({ statistics: 'Statistics', }; - if (errors.statistics) { - const newErrorStatistics = []; - const formStatistics = get(state.form.statistics); - - formStatistics.forEach((formStatistic, index) => { - const errorStatistic = errors.statistics.find(s => s.index === index); - if (errorStatistic) { - if (formStatistic.yearOrPeriod === 'Year') { - newErrorStatistics.push({ - enterAllValues: - 'Enter year, deficiency amount, and total penalties', - index, - }); - } else { - newErrorStatistics.push({ - enterAllValues: - 'Enter period, deficiency amount, and total penalties', - index, - }); - } - } else { - newErrorStatistics.push({}); - } - }); - - errors.statistics = newErrorStatistics; + const statisticsErrors = aggregateStatisticsErrors({ errors, get }); + if (statisticsErrors) { + errors.statistics = statisticsErrors; } return path.error({ diff --git a/web-client/src/presenter/actions/validatePetitionFromPaperAction.test.js b/web-client/src/presenter/actions/validatePetitionFromPaperAction.test.js index 15b34217951..47f00226550 100644 --- a/web-client/src/presenter/actions/validatePetitionFromPaperAction.test.js +++ b/web-client/src/presenter/actions/validatePetitionFromPaperAction.test.js @@ -58,7 +58,7 @@ describe('validatePetitionFromPaperAction', () => { .validatePetitionFromPaperInteractor.mockReturnValue({ statistics: [ { deficiency: 'enter deficiency amount', index: 1 }, - { index: 2, totalPenalties: 'enter total penalties' }, + { index: 2, irsTotalPenalties: 'enter total penalties' }, ], }); diff --git a/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.js b/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.js index 470616fefa4..cd2f84ddb15 100644 --- a/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.js +++ b/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.js @@ -1,4 +1,10 @@ import { Document } from '../../../../../shared/src/business/entities/Document'; +import { + OPINION_DOCUMENT_TYPES, + ORDER_DOCUMENT_TYPES, +} from '../../../../../shared/src/business/entities/EntityConstants'; + +import { capitalize } from 'lodash'; import { paginationHelper } from './advancedSearchHelper'; import { state } from 'cerebral'; @@ -6,6 +12,16 @@ export const advancedDocumentSearchHelper = (get, applicationContext) => { let paginatedResults = {}; const searchResults = get(state.searchResults); const isPublic = get(state.isPublic); + const advancedSearchTab = get(state.advancedSearchTab); + const searchTabs = applicationContext.getConstants().ADVANCED_SEARCH_TABS; + + let showSealedIcon = true; + let documentTypeVerbiage = capitalize(advancedSearchTab); + + if (advancedSearchTab === searchTabs.OPINION) { + showSealedIcon = false; + documentTypeVerbiage = `${documentTypeVerbiage} type`; + } if (searchResults) { paginatedResults = paginationHelper( @@ -16,18 +32,24 @@ export const advancedDocumentSearchHelper = (get, applicationContext) => { paginatedResults.formattedSearchResults = paginatedResults.searchResults.map( searchResult => - formatDocumentSearchResultRecord(searchResult, { applicationContext }), + formatDocumentSearchResultRecord(get, searchResult, advancedSearchTab, { + applicationContext, + }), ); } return { ...paginatedResults, + documentTypeVerbiage, isPublic, + showSealedIcon, }; }; export const formatDocumentSearchResultRecord = ( + get, result, + advancedSearchTab, { applicationContext }, ) => { result.formattedFiledDate = applicationContext @@ -35,18 +57,18 @@ export const formatDocumentSearchResultRecord = ( .formatDateString(result.filingDate, 'MMDDYY'); result.caseTitle = applicationContext.getCaseTitle(result.caseCaption || ''); + result.formattedDocumentType = Document.getFormattedType(result.documentType); - const eventCodeAndDocumentType = result.documentType.split('-'); - result.formattedEventCode = eventCodeAndDocumentType[0].trim(); - result.formattedDocumentType = eventCodeAndDocumentType[1].trim(); + const searchTabs = applicationContext.getConstants().ADVANCED_SEARCH_TABS; + if (advancedSearchTab === searchTabs.OPINION) { + result.documentTitle = result.formattedDocumentType; + } - if (Document.OPINION_DOCUMENT_TYPES.includes(result.formattedEventCode)) { + if (OPINION_DOCUMENT_TYPES.includes(result.eventCode)) { result.formattedJudgeName = result.judge ? applicationContext.getUtilities().getJudgeLastName(result.judge) : ''; - } else if ( - Document.ORDER_DOCUMENT_TYPES.includes(result.formattedEventCode) - ) { + } else if (ORDER_DOCUMENT_TYPES.includes(result.eventCode)) { result.formattedSignedJudgeName = result.signedJudgeName ? applicationContext .getUtilities() diff --git a/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.test.js b/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.test.js index fce2178b801..463865a7009 100644 --- a/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.test.js +++ b/web-client/src/presenter/computeds/AdvancedSearch/advancedDocumentSearchHelper.test.js @@ -19,20 +19,56 @@ describe('advancedDocumentSearchHelper', () => { }, ); - it('returns an empty object when searchResults is undefined', () => { + it('returns capitalized document type verbiage and isPublic when both the form and searchResults are empty and the search tab is opinion', () => { const result = runCompute(advancedDocumentSearchHelper, { state: { advancedSearchForm: {}, + advancedSearchTab: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS.OPINION, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, + isPublic: true, }, }); - expect(result).toEqual({}); + expect(result).toEqual({ + documentTypeVerbiage: 'Opinion type', + isPublic: true, + showSealedIcon: false, + }); + }); + + it('returns capitalized document type verbiage and isPublic when both the form and searchResults are empty and the search tab is order', () => { + const result = runCompute(advancedDocumentSearchHelper, { + state: { + advancedSearchForm: {}, + advancedSearchTab: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS.ORDER, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, + isPublic: true, + }, + }); + + expect(result).toEqual({ + documentTypeVerbiage: 'Order', + isPublic: true, + showSealedIcon: true, + }); }); it('returns showNoMatches true and showSearchResults false when searchResults are empty', () => { const result = runCompute(advancedDocumentSearchHelper, { state: { advancedSearchForm: { currentPage: 1 }, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, searchResults: [], }, }); @@ -48,6 +84,10 @@ describe('advancedDocumentSearchHelper', () => { const result = runCompute(advancedDocumentSearchHelper, { state: { advancedSearchForm: { currentPage: 1 }, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, searchResults: [], }, }); @@ -59,6 +99,10 @@ describe('advancedDocumentSearchHelper', () => { const result = runCompute(advancedDocumentSearchHelper, { state: { advancedSearchForm: { currentPage: 1 }, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, isPublic: true, searchResults: [], }, @@ -71,6 +115,10 @@ describe('advancedDocumentSearchHelper', () => { const result = runCompute(advancedDocumentSearchHelper, { state: { advancedSearchForm: { currentPage: 1 }, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, searchResults: [ { docketNumber: '101-19', @@ -97,6 +145,12 @@ describe('advancedDocumentSearchHelper', () => { const result = runCompute(advancedDocumentSearchHelper, { state: { advancedSearchForm: { currentPage: 1 }, + advancedSearchTab: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS.ORDER, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, searchResults: [ { caseCaption: 'Test Petitioner, Petitioner', @@ -134,7 +188,6 @@ describe('advancedDocumentSearchHelper', () => { documentTitle: 'Order', filingDate: '2019-03-01T05:00:00.000Z', formattedDocumentType: 'Order', - formattedEventCode: 'O', formattedFiledDate: '03/01/19', judge: 'Judge Buch', }, @@ -148,7 +201,6 @@ describe('advancedDocumentSearchHelper', () => { documentType: 'OAPF - Order for Amended Petition and Filing Fee', filingDate: '2019-03-01T05:00:00.000Z', formattedDocumentType: 'Order for Amended Petition and Filing Fee', - formattedEventCode: 'OAPF', formattedFiledDate: '03/01/19', judge: 'Judge Cohen', }, @@ -159,6 +211,12 @@ describe('advancedDocumentSearchHelper', () => { const result = runCompute(advancedDocumentSearchHelper, { state: { advancedSearchForm: { currentPage: 1 }, + advancedSearchTab: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS.OPINION, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, searchResults: [ { caseCaption: 'Test Petitioner, Petitioner', @@ -178,7 +236,7 @@ describe('advancedDocumentSearchHelper', () => { docketNumberWithSuffix: '102-19P', documentContents: 'Test Petitioner, Petitioner', documentTitle: 'Opinion for Stuff', - documentType: 'TCOP - T.C. Opinion', + documentType: 'Summary Opinion', filingDate: '2019-03-01T05:00:00.000Z', judge: 'Judge Cohen', }, @@ -193,11 +251,10 @@ describe('advancedDocumentSearchHelper', () => { docketNumberSuffix: 'Z', docketNumberWithSuffix: '101-19Z', documentContents: 'Test Petitioner, Petitioner', - documentTitle: 'My Opinion', + documentTitle: 'T.C. Opinion', documentType: 'TCOP - T.C. Opinion', filingDate: '2019-03-01T05:00:00.000Z', formattedDocumentType: 'T.C. Opinion', - formattedEventCode: 'TCOP', formattedFiledDate: '03/01/19', judge: 'Judge Buch', }, @@ -207,14 +264,44 @@ describe('advancedDocumentSearchHelper', () => { docketNumberSuffix: 'P', docketNumberWithSuffix: '102-19P', documentContents: 'Test Petitioner, Petitioner', - documentTitle: 'Opinion for Stuff', - documentType: 'TCOP - T.C. Opinion', + documentTitle: 'Summary Opinion', + documentType: 'Summary Opinion', filingDate: '2019-03-01T05:00:00.000Z', - formattedDocumentType: 'T.C. Opinion', - formattedEventCode: 'TCOP', + formattedDocumentType: 'Summary Opinion', formattedFiledDate: '03/01/19', judge: 'Judge Cohen', }, ]); }); + + it('does not show sealed case icon for public opinion search', () => { + const result = runCompute(advancedDocumentSearchHelper, { + state: { + advancedSearchTab: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS.OPINION, + constants: { + ADVANCED_SEARCH_TABS: applicationContext.getConstants() + .ADVANCED_SEARCH_TABS, + }, + isPublic: true, + searchResults: [ + { + docketNumber: '101-19', + docketNumberSuffix: 'Z', + documentContents: 'Test Petitioner, Petitioner', + documentTitle: 'Order', + documentType: 'O - Order', + filingDate: '2019-03-01T05:00:00.000Z', + isSealed: true, + judge: 'Judge Buch', + }, + ], + }, + }); + + expect(result).toMatchObject({ + searchResultsCount: 1, + showSealedIcon: false, + }); + }); }); diff --git a/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.js b/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.js index 2ecdd58ee5a..808c8c63847 100644 --- a/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.js +++ b/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.js @@ -39,7 +39,7 @@ export const advancedSearchHelper = (get, applicationContext) => { COUNTRY_TYPES, } = applicationContext.getConstants(); const searchResults = get(state.searchResults); - const advancedSearchTab = get(state.advancedSearchTab); + const advancedSearchTab = get(state.advancedSearchTab) || 'case'; // 'case' is default tab, but sometimes undefined in state. const currentPage = get(state.advancedSearchForm.currentPage); let result = { showPractitionerSearch: permissions.MANAGE_PRACTITIONER_USERS, diff --git a/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.test.js b/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.test.js index 817deecd83d..0e6f935493d 100644 --- a/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.test.js +++ b/web-client/src/presenter/computeds/AdvancedSearch/advancedSearchHelper.test.js @@ -1,5 +1,7 @@ -import { ContactFactory } from '../../../../../shared/src/business/entities/contacts/ContactFactory'; -import { User } from '../../../../../shared/src/business/entities/User'; +import { + COUNTRY_TYPES, + ROLES, +} from '../../../../../shared/src/business/entities/EntityConstants'; import { advancedSearchHelper as advancedSearchHelperComputed } from './advancedSearchHelper'; import { applicationContext } from '../../../applicationContext'; import { getUserPermissions } from '../../../../../shared/src/authorization/getUserPermissions'; @@ -32,7 +34,7 @@ describe('advancedSearchHelper', () => { beforeEach(() => { globalUser = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketClerk', }; }); @@ -64,7 +66,7 @@ describe('advancedSearchHelper', () => { it('returns showPractitionerSearch false when user is an external user', () => { globalUser = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: 'practitioner', }; @@ -85,7 +87,7 @@ describe('advancedSearchHelper', () => { ...getBaseState(globalUser), advancedSearchForm: { caseSearchByName: { - countryType: ContactFactory.COUNTRY_TYPES.DOMESTIC, + countryType: COUNTRY_TYPES.DOMESTIC, }, }, }, @@ -102,7 +104,7 @@ describe('advancedSearchHelper', () => { ...getBaseState(globalUser), advancedSearchForm: { caseSearchByName: { - countryType: ContactFactory.COUNTRY_TYPES.INTERNATIONAL, + countryType: COUNTRY_TYPES.INTERNATIONAL, }, }, }, diff --git a/web-client/src/presenter/computeds/Dashboard/externalUserCasesHelper.js b/web-client/src/presenter/computeds/Dashboard/externalUserCasesHelper.js new file mode 100644 index 00000000000..6bbcf7b7ede --- /dev/null +++ b/web-client/src/presenter/computeds/Dashboard/externalUserCasesHelper.js @@ -0,0 +1,33 @@ +import { state } from 'cerebral'; + +export const externalUserCasesHelper = (get, applicationContext) => { + const { formatCase } = applicationContext.getUtilities(); + + const openCases = get(state.openCases); + const closedCases = get(state.closedCases); + + const formattedOpenCases = openCases.map(openCase => + formatCase(applicationContext, openCase), + ); + const formattedClosedCases = closedCases.map(closedCase => + formatCase(applicationContext, closedCase), + ); + + const openCurrentPage = get(state.openCasesCurrentPage) || 1; + const closedCurrentPage = get(state.closedCasesCurrentPage) || 1; + const pageSize = get(state.constants.CASE_LIST_PAGE_SIZE); + + return { + closedCaseResults: formattedClosedCases.slice( + 0, + closedCurrentPage * pageSize, + ), + closedCasesCount: formattedClosedCases.length, + openCaseResults: formattedOpenCases.slice(0, openCurrentPage * pageSize), + openCasesCount: formattedOpenCases.length, + showLoadMoreClosedCases: + formattedClosedCases.length > closedCurrentPage * pageSize, + showLoadMoreOpenCases: + formattedOpenCases.length > openCurrentPage * pageSize, + }; +}; diff --git a/web-client/src/presenter/computeds/Dashboard/externalUserCasesHelper.test.js b/web-client/src/presenter/computeds/Dashboard/externalUserCasesHelper.test.js new file mode 100644 index 00000000000..43d7517d501 --- /dev/null +++ b/web-client/src/presenter/computeds/Dashboard/externalUserCasesHelper.test.js @@ -0,0 +1,97 @@ +import { applicationContext } from '../../../applicationContext'; +import { externalUserCasesHelper as externalUserCasesHelperComputed } from './externalUserCasesHelper'; +import { runCompute } from 'cerebral/test'; +import { withAppContextDecorator } from '../../../withAppContext'; + +const externalUserCasesHelper = withAppContextDecorator( + externalUserCasesHelperComputed, + { + ...applicationContext, + getUtilities: () => ({ + formatCase: () => ({ + caseId: 'case-id-123', + }), + }), + }, +); + +const baseState = { + closedCases: [...Array(10).keys()], + constants: { + CASE_LIST_PAGE_SIZE: 5, + }, + openCases: [...Array(10).keys()], +}; + +describe('externalUserCasesHelper', () => { + it('returns more than 10 open and closed cases, shows load more button for both', () => { + const result = runCompute(externalUserCasesHelper, { + state: { + ...baseState, + }, + }); + + expect(result).toMatchObject({ + showLoadMoreClosedCases: true, + showLoadMoreOpenCases: true, + }); + }); + + it("doesn't show load more button for closed cases", () => { + const result = runCompute(externalUserCasesHelper, { + state: { + ...baseState, + closedCases: [0], + }, + }); + + expect(result).toMatchObject({ + showLoadMoreClosedCases: false, + showLoadMoreOpenCases: true, + }); + }); + + it("doesn't show load more button for open cases", () => { + const result = runCompute(externalUserCasesHelper, { + state: { + ...baseState, + openCases: [0], + }, + }); + + expect(result).toMatchObject({ + showLoadMoreClosedCases: true, + showLoadMoreOpenCases: false, + }); + }); + + it('sets the total count of both open and closed cases', () => { + const result = runCompute(externalUserCasesHelper, { + state: { + ...baseState, + closedCases: [{ caseId: 'hey' }], + openCases: [{ caseId: 'hey' }, { caseId: 'bye' }], + }, + }); + + expect(result).toMatchObject({ + closedCasesCount: 1, + openCasesCount: 2, + }); + }); + + it('uses current pages in state', () => { + const result = runCompute(externalUserCasesHelper, { + state: { + ...baseState, + closedCasesCurrentPage: 2, + openCasesCurrentPage: 2, + }, + }); + + expect(result).toMatchObject({ + showLoadMoreClosedCases: false, + showLoadMoreOpenCases: false, + }); + }); +}); diff --git a/web-client/src/presenter/computeds/addDocketEntryHelper.test.js b/web-client/src/presenter/computeds/addDocketEntryHelper.test.js index 0a1efe38e84..fc6b8c49479 100644 --- a/web-client/src/presenter/computeds/addDocketEntryHelper.test.js +++ b/web-client/src/presenter/computeds/addDocketEntryHelper.test.js @@ -1,5 +1,5 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; +import { PARTY_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { addDocketEntryHelper as addDocketEntryHelperComputed } from './addDocketEntryHelper'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; @@ -71,7 +71,7 @@ describe('addDocketEntryHelper', () => { }); it('shows secondary party for petitionerSpouse or petitionerDeceasedSpouse', () => { - state.caseDetail.partyType = ContactFactory.PARTY_TYPES.petitionerSpouse; + state.caseDetail.partyType = PARTY_TYPES.petitionerSpouse; const result = runCompute(addDocketEntryHelper, { state }); expect(result.showSecondaryParty).toBeTruthy(); }); diff --git a/web-client/src/presenter/computeds/addEditUserCaseNoteModalHelper.test.js b/web-client/src/presenter/computeds/addEditUserCaseNoteModalHelper.test.js index 4deb6b1bb54..cc0b9760da1 100644 --- a/web-client/src/presenter/computeds/addEditUserCaseNoteModalHelper.test.js +++ b/web-client/src/presenter/computeds/addEditUserCaseNoteModalHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { runCompute } from 'cerebral/test'; import { withAppContextDecorator } from '../../withAppContext'; @@ -10,7 +10,7 @@ const addEditUserCaseNoteModalHelper = withAppContextDecorator( addEditUserCaseNoteModalHelperComputed, { getConstants: () => ({ - USER_ROLES: User.ROLES, + USER_ROLES: ROLES, }), getCurrentUser: () => currentUser, }, @@ -19,7 +19,7 @@ const addEditUserCaseNoteModalHelper = withAppContextDecorator( describe('addEditUserCaseNoteModalHelper', () => { it("should return Judge's notes as notesLabel if current user is not a trial clerk", () => { currentUser = { - role: User.ROLES.judge, + role: ROLES.judge, }; const result = runCompute(addEditUserCaseNoteModalHelper, {}); @@ -29,7 +29,7 @@ describe('addEditUserCaseNoteModalHelper', () => { it('should return Notes as notesLabel if current user is a trial clerk', () => { currentUser = { - role: User.ROLES.trialClerk, + role: ROLES.trialClerk, }; const result = runCompute(addEditUserCaseNoteModalHelper, {}); diff --git a/web-client/src/presenter/computeds/alertHelper.js b/web-client/src/presenter/computeds/alertHelper.js index 5e7bff0bbc5..beb4e259198 100644 --- a/web-client/src/presenter/computeds/alertHelper.js +++ b/web-client/src/presenter/computeds/alertHelper.js @@ -7,6 +7,7 @@ export const alertHelper = (get, applicationContext) => { return { messagesDeduped: uniq(alertError.messages), + preventAutoScroll: false, showErrorAlert: !!alertError.title || !!alertError.message || !!alertError.messages, showLogIn: userIsIdentified, diff --git a/web-client/src/presenter/computeds/caseDetailEditContactsHelper.test.js b/web-client/src/presenter/computeds/caseDetailEditContactsHelper.test.js index 3cab15aa76e..562c7756ad9 100644 --- a/web-client/src/presenter/computeds/caseDetailEditContactsHelper.test.js +++ b/web-client/src/presenter/computeds/caseDetailEditContactsHelper.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { caseDetailEditContactsHelper as caseDetailEditContactsHelperComputed } from './caseDetailEditContactsHelper'; import { runCompute } from 'cerebral/test'; @@ -13,7 +13,7 @@ describe('caseDetailEditContactsHelper', () => { it('should validate form view information for party type Conservator', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.conservator }, + form: { partyType: PARTY_TYPES.conservator }, }, }); expect(result).toMatchObject({ @@ -29,7 +29,7 @@ describe('caseDetailEditContactsHelper', () => { it('should validate form view information for party type Corporation', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.corporation }, + form: { partyType: PARTY_TYPES.corporation }, }, }); expect(result).toMatchObject({ @@ -44,7 +44,7 @@ describe('caseDetailEditContactsHelper', () => { it('should validate form view information for party type Custodian', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.custodian }, + form: { partyType: PARTY_TYPES.custodian }, }, }); expect(result).toMatchObject({ @@ -60,7 +60,7 @@ describe('caseDetailEditContactsHelper', () => { it('should validate form view information for party type Donor', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.donor }, + form: { partyType: PARTY_TYPES.donor }, }, }); expect(result).toMatchObject({ @@ -75,7 +75,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, }, }, }); @@ -94,7 +94,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }, }, }); @@ -111,7 +111,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }, }, }); @@ -129,7 +129,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + partyType: PARTY_TYPES.nextFriendForIncompetentPerson, }, }, }); @@ -147,7 +147,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, }, }, }); @@ -165,7 +165,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, }, }, }); @@ -183,7 +183,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + partyType: PARTY_TYPES.partnershipOtherThanTaxMatters, }, }, }); @@ -201,7 +201,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, }, }, }); @@ -219,7 +219,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -235,7 +235,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }, }, }); @@ -259,7 +259,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }, }, }); @@ -279,7 +279,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.survivingSpouse, + partyType: PARTY_TYPES.survivingSpouse, }, }, }); @@ -297,7 +297,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, }, }, }); @@ -313,7 +313,7 @@ describe('caseDetailEditContactsHelper', () => { const result = runCompute(caseDetailEditContactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, }, }, }); diff --git a/web-client/src/presenter/computeds/caseDetailEditHelper.js b/web-client/src/presenter/computeds/caseDetailEditHelper.js index 2d16344429f..6c7a09a8200 100644 --- a/web-client/src/presenter/computeds/caseDetailEditHelper.js +++ b/web-client/src/presenter/computeds/caseDetailEditHelper.js @@ -49,9 +49,14 @@ export const caseDetailEditHelper = (get, applicationContext) => { } } + const receivedAtFormatted = applicationContext + .getUtilities() + .formatDateString(caseDetail.receivedAt, 'MM/DD/YYYY'); + return { ownershipDisclosureStatementDocumentId, partyTypes: PARTY_TYPES, + receivedAtFormatted, requestForPlaceOfTrialDocumentId, requestForPlaceOfTrialDocumentTitle, shouldShowIrsNoticeDate: caseDetail.hasVerifiedIrsNotice, diff --git a/web-client/src/presenter/computeds/caseDetailEditHelper.test.js b/web-client/src/presenter/computeds/caseDetailEditHelper.test.js index 0613dde159d..35a665da235 100644 --- a/web-client/src/presenter/computeds/caseDetailEditHelper.test.js +++ b/web-client/src/presenter/computeds/caseDetailEditHelper.test.js @@ -1,5 +1,7 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; +import { + PARTY_TYPES, + PAYMENT_STATUS, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { caseDetailEditHelper as caseDetailEditHelperComputed } from './caseDetailEditHelper'; import { runCompute } from 'cerebral/test'; @@ -15,7 +17,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.conservator, + partyType: PARTY_TYPES.conservator, }, }, }); @@ -26,7 +28,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.conservator, + partyType: PARTY_TYPES.conservator, }, }, }); @@ -38,7 +40,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); @@ -50,7 +52,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.custodian, + partyType: PARTY_TYPES.custodian, }, }, }); @@ -62,7 +64,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.donor, + partyType: PARTY_TYPES.donor, }, }, }); @@ -74,7 +76,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, }, }, }); @@ -86,7 +88,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }, }, }); @@ -98,7 +100,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }, }, }); @@ -110,7 +112,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + partyType: PARTY_TYPES.nextFriendForIncompetentPerson, }, }, }); @@ -122,7 +124,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, }, }, }); @@ -134,7 +136,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, }, }, }); @@ -146,7 +148,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, }, }, }); @@ -158,7 +160,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + partyType: PARTY_TYPES.partnershipOtherThanTaxMatters, }, }, }); @@ -170,7 +172,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -182,7 +184,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }, }, }); @@ -194,7 +196,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }, }, }); @@ -206,7 +208,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.survivingSpouse, + partyType: PARTY_TYPES.survivingSpouse, }, }, }); @@ -218,7 +220,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, }, }, }); @@ -230,7 +232,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, }, }, }); @@ -248,7 +250,7 @@ describe('case detail edit computed', () => { documentType: 'Ownership Disclosure Statement', }, ], - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); @@ -268,7 +270,7 @@ describe('case detail edit computed', () => { documentType: 'Petition', }, ], - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); @@ -279,7 +281,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -290,7 +292,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, preferredTrialCity: 'Fresno, California', }, }, @@ -312,7 +314,7 @@ describe('case detail edit computed', () => { }, ], isPaper: true, - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -329,7 +331,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - petitionPaymentStatus: Case.PAYMENT_STATUS.UNPAID, + petitionPaymentStatus: PAYMENT_STATUS.UNPAID, }, }, }); @@ -340,7 +342,7 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - petitionPaymentStatus: Case.PAYMENT_STATUS.PAID, + petitionPaymentStatus: PAYMENT_STATUS.PAID, }, }, }); @@ -351,10 +353,21 @@ describe('case detail edit computed', () => { const result = runCompute(caseDetailEditHelper, { state: { form: { - petitionPaymentStatus: Case.PAYMENT_STATUS.WAIVED, + petitionPaymentStatus: PAYMENT_STATUS.WAIVED, }, }, }); expect(result.showOrderForFilingFee).toBeFalsy(); }); + + it('sets receivedAtFormatted to formatted string', () => { + const result = runCompute(caseDetailEditHelper, { + state: { + form: { + receivedAt: '2001-12-01T20:00:00.000Z', + }, + }, + }); + expect(result.receivedAtFormatted).toEqual('12/01/2001'); + }); }); diff --git a/web-client/src/presenter/computeds/caseDetailHeaderHelper.test.js b/web-client/src/presenter/computeds/caseDetailHeaderHelper.test.js index 7ea40b60a53..7f8e2ca0732 100644 --- a/web-client/src/presenter/computeds/caseDetailHeaderHelper.test.js +++ b/web-client/src/presenter/computeds/caseDetailHeaderHelper.test.js @@ -1,5 +1,7 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { caseDetailHeaderHelper as caseDetailHeaderHelperComputed } from './caseDetailHeaderHelper'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; @@ -28,13 +30,13 @@ const getBaseState = user => { describe('caseDetailHeaderHelper', () => { it('should set showEditCaseButton to true if the user has UPDATE_CASE_CONTENT permission', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: 'docketClerk', }; const result = runCompute(caseDetailHeaderHelper, { state: { ...getBaseState(user), - caseDetail: { status: Case.STATUS_TYPES.new }, + caseDetail: { status: CASE_STATUS_TYPES.new }, }, }); expect(result.showEditCaseButton).toEqual(true); @@ -42,13 +44,13 @@ describe('caseDetailHeaderHelper', () => { it('should set showEditCaseButton to false if the user does not have UPDATE_CASE_CONTENT permission', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { state: { ...getBaseState(user), - caseDetail: { status: Case.STATUS_TYPES.new }, + caseDetail: { status: CASE_STATUS_TYPES.new }, }, }); expect(result.showEditCaseButton).toEqual(false); @@ -56,7 +58,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showFileFirstDocumentButton and showRequestAccessToCaseButton to false if user role is respondent and the respondent is associated with the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHeaderHelper, { @@ -76,7 +78,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showFileFirstDocumentButton and showRequestAccessToCaseButton to false if user role is respondent and the respondent is not associated with the case but the case is sealed', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHeaderHelper, { @@ -99,7 +101,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showRequestAccessToCaseButton to true if user role is respondent and the respondent is not associated with the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHeaderHelper, { @@ -119,7 +121,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showFileFirstDocumentButton to true if user role is respondent and there is no respondent associated with the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHeaderHelper, { @@ -139,7 +141,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showPendingAccessToCaseButton to true if user role is practitioner and case is not owned by user but has pending request', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -159,7 +161,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showRequestAccessToCaseButton to true if user role is practitioner and case is not owned by user', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -178,7 +180,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showRequestAccessToCaseButton to false when the current page is FilePetitionSuccess', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -197,7 +199,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showRequestAccessToCaseButton to false if user role is practitioner and case is not owned by user and the case is sealed', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -216,7 +218,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showRequestAccessToCaseButton to false if user role is practitioner and case is owned by user', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -235,7 +237,7 @@ describe('caseDetailHeaderHelper', () => { it('should set showRequestAccessToCaseButton to false if user role is petitioner and user is not associated with the case', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -254,7 +256,7 @@ describe('caseDetailHeaderHelper', () => { it('should show the consolidated case icon if the case is associated with a lead case', async () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -276,7 +278,7 @@ describe('caseDetailHeaderHelper', () => { it('should NOT show the consolidated case icon if the case is NOT associated with a lead case', async () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -298,7 +300,7 @@ describe('caseDetailHeaderHelper', () => { it('should show the case detail header menu and add docket entry and create order buttons if current page is CaseDetailInternal and user role is docketclerk', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseDetailHeaderHelper, { @@ -373,7 +375,7 @@ describe('caseDetailHeaderHelper', () => { it('should show the Upload PDF button in the action menu if the user is a court user', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { @@ -394,7 +396,7 @@ describe('caseDetailHeaderHelper', () => { it('should NOT show the Upload PDF button in the action menu if the user is not a court user', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '123', }; const result = runCompute(caseDetailHeaderHelper, { diff --git a/web-client/src/presenter/computeds/caseDetailHelper.js b/web-client/src/presenter/computeds/caseDetailHelper.js index b54e1f95f7b..ecd80a0cff6 100644 --- a/web-client/src/presenter/computeds/caseDetailHelper.js +++ b/web-client/src/presenter/computeds/caseDetailHelper.js @@ -105,6 +105,7 @@ export const caseDetailHelper = (get, applicationContext) => { modalState && modalState.respondentMatches && modalState.respondentMatches.length, + showAddCorrespondenceButton: permissions.CASE_CORRESPONDENCE, showCaseDeadlinesExternal, showCaseDeadlinesInternal, showCaseDeadlinesInternalEmpty, diff --git a/web-client/src/presenter/computeds/caseDetailHelper.test.js b/web-client/src/presenter/computeds/caseDetailHelper.test.js index 3f9fc8d904d..f9949c32992 100644 --- a/web-client/src/presenter/computeds/caseDetailHelper.test.js +++ b/web-client/src/presenter/computeds/caseDetailHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { caseDetailHelper as caseDetailHelperComputed } from './caseDetailHelper'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; @@ -22,9 +22,51 @@ const getBaseState = user => { }; describe('case detail computed', () => { + it('should set showAddCorrespondenceButton to false when the current user is not a petitions clerk or a docket clerk', () => { + const user = { + role: ROLES.privatePractitioner, + userId: '123', + }; + + const result = runCompute(caseDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { privatePractitioners: [] }, + currentPage: 'CaseDetail', + form: {}, + screenMetadata: { + isAssociated: false, + }, + }, + }); + + expect(result.showAddCorrespondenceButton).toEqual(false); + }); + + it('should set showAddCorrespondenceButton to true when the current user is a petitions clerk', () => { + const user = { + role: ROLES.petitionsClerk, + userId: '123', + }; + + const result = runCompute(caseDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { privatePractitioners: [] }, + currentPage: 'CaseDetail', + form: {}, + screenMetadata: { + isAssociated: false, + }, + }, + }); + + expect(result.showAddCorrespondenceButton).toEqual(true); + }); + it('should set showFileDocumentButton to true if current page is CaseDetail, user role is practitioner, and case is owned by user', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -43,7 +85,7 @@ describe('case detail computed', () => { it('should set showFileDocumentButton to false if current page is CaseDetail, user role is practitioner, and case is not owned by user', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -62,7 +104,7 @@ describe('case detail computed', () => { it('should set showFileDocumentButton to true if current page is CaseDetail, user role is petitioner, and the user is associated with the case', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, }; const result = runCompute(caseDetailHelper, { state: { @@ -80,7 +122,7 @@ describe('case detail computed', () => { it('should set userHasAccessToCase to true if user role is petitioner and user is associated with case', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -99,7 +141,7 @@ describe('case detail computed', () => { it('should set userHasAccessToCase to true if user role is practitioner and the practitioner is associated with the case', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -116,7 +158,7 @@ describe('case detail computed', () => { it('should set userHasAccessToCase to false if user role is practitioner and the practitioner is not associated with the case', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -133,7 +175,7 @@ describe('case detail computed', () => { it('should set userHasAccessToCase and showFileDocumentButton to true if user role is respondent and the respondent is associated with the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -153,7 +195,7 @@ describe('case detail computed', () => { it('should set userHasAccessToCase and showFileDocumentButton to false if user role is respondent and the respondent is not associated with the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -173,7 +215,7 @@ describe('case detail computed', () => { it('should set userHasAccessToCase and showFileDocumentButton to false if user role is respondent and there is no respondent associated with the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -193,7 +235,7 @@ describe('case detail computed', () => { it('should show case deadlines external view for external user who is associated with the case if there are deadlines on the case', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -215,7 +257,7 @@ describe('case detail computed', () => { it('should not show case deadlines external view for external user who is associated with the case if there are not deadlines on the case', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -237,7 +279,7 @@ describe('case detail computed', () => { it('should not show case deadlines external view for external user who is not associated with the case and there are deadlines on the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -259,7 +301,7 @@ describe('case detail computed', () => { it('should show case deadlines internal view and not show case deadlines external view for internal user', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -278,7 +320,7 @@ describe('case detail computed', () => { it('should show case deadlines internal view as empty and not show case deadlines external view for internal user if case deadlines is empty', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -297,7 +339,7 @@ describe('case detail computed', () => { it('should show practitioner section if user is an internal user', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -312,7 +354,7 @@ describe('case detail computed', () => { it('should show practitioner section if user is an external user and there are privatePractitioners on the case', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -327,7 +369,7 @@ describe('case detail computed', () => { it('should not show practitioner section if user is an external user and there are no privatePractitioners on the case', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -342,7 +384,7 @@ describe('case detail computed', () => { it('should show respondent section if user is an internal user', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -357,7 +399,7 @@ describe('case detail computed', () => { it('should show respondent section if user is an external user and there are irsPractitioners on the case', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -372,7 +414,7 @@ describe('case detail computed', () => { it('should not show respondent section if user is an external user and there are no irsPractitioners on the case', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -387,7 +429,7 @@ describe('case detail computed', () => { it('should format practitioner matches with cityStateZip string and isAlreadyInCase boolean', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -431,7 +473,7 @@ describe('case detail computed', () => { it('should set practitionerSearchResultsCount to the length of the state.modal.practitionerMatches', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -447,7 +489,7 @@ describe('case detail computed', () => { it('should set practitionerSearchResultsCount to 0 if the state.modal.practitionerMatches is an empty array', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -463,7 +505,7 @@ describe('case detail computed', () => { it('should not set practitionerSearchResultsCount if state.modal is an empty object', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -479,7 +521,7 @@ describe('case detail computed', () => { it('should format respondent matches with cityStateZip string and isAlreadyInCase boolean', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -523,7 +565,7 @@ describe('case detail computed', () => { it('should set respondentSearchResultsCount to the length of the state.modal.respondentMatches', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -539,7 +581,7 @@ describe('case detail computed', () => { it('should set respondentSearchResultsCount to 0 if the state.modal.respondentMatches is an empty array', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -555,7 +597,7 @@ describe('case detail computed', () => { it('should not set respondentSearchResultsCount if state.modal is an empty object', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -571,7 +613,7 @@ describe('case detail computed', () => { it('should show empty state for consolidated cases', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseDetailHelper, { @@ -598,7 +640,7 @@ describe('case detail computed', () => { it('should show edit petition details button if user has EDIT_PETITION_DETAILS permission', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -615,7 +657,7 @@ describe('case detail computed', () => { it('should not show edit petition details button if user does not have EDIT_PETITION_DETAILS permission', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -632,7 +674,7 @@ describe('case detail computed', () => { it('should show the filing fee section for a petitioner user', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -648,7 +690,7 @@ describe('case detail computed', () => { it('should show the filing fee section for a practitioner user', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { @@ -664,7 +706,7 @@ describe('case detail computed', () => { it('should not show the filing fee section for a respondent user', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '789', }; const result = runCompute(caseDetailHelper, { diff --git a/web-client/src/presenter/computeds/caseDetailSubnavHelper.test.js b/web-client/src/presenter/computeds/caseDetailSubnavHelper.test.js index a95cbef40a1..f3e6aaaa7db 100644 --- a/web-client/src/presenter/computeds/caseDetailSubnavHelper.test.js +++ b/web-client/src/presenter/computeds/caseDetailSubnavHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { caseDetailSubnavHelper as caseDetailSubnavHelperComputed } from './caseDetailSubnavHelper'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; @@ -27,7 +27,7 @@ const getBaseState = user => { describe('caseDetailSubnavHelper', () => { it('should show internal-only tabs if user is internal', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(caseDetailSubnavHelper, { @@ -44,7 +44,7 @@ describe('caseDetailSubnavHelper', () => { it('should not show internal-only tabs if user is external', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '123', }; const result = runCompute(caseDetailSubnavHelper, { @@ -60,7 +60,7 @@ describe('caseDetailSubnavHelper', () => { it('should show case information tab if user is external and associated with the case', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '123', }; const result = runCompute(caseDetailSubnavHelper, { @@ -76,7 +76,7 @@ describe('caseDetailSubnavHelper', () => { it('should not show case information tab if user is external and not associated with the case', () => { const user = { - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '123', }; const result = runCompute(caseDetailSubnavHelper, { diff --git a/web-client/src/presenter/computeds/caseInformationHelper.test.js b/web-client/src/presenter/computeds/caseInformationHelper.test.js index ef6a4446f80..c495a42a0a8 100644 --- a/web-client/src/presenter/computeds/caseInformationHelper.test.js +++ b/web-client/src/presenter/computeds/caseInformationHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { caseInformationHelper } from './caseInformationHelper'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; import { runCompute } from 'cerebral/test'; @@ -12,7 +12,7 @@ const getBaseState = user => { describe('case information helper', () => { it('should show add counsel section if user is an internal user', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseInformationHelper, { @@ -27,7 +27,7 @@ describe('case information helper', () => { it('should not show add counsel section if user is an external user', () => { const user = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, userId: '123', }; const result = runCompute(caseInformationHelper, { @@ -42,7 +42,7 @@ describe('case information helper', () => { it('should show edit privatePractitioners and irsPractitioners buttons if user is an internal user and there are privatePractitioners and irsPractitioners on the case', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseInformationHelper, { @@ -61,7 +61,7 @@ describe('case information helper', () => { it('should not show edit privatePractitioners or irsPractitioners buttons if user is an internal user and there are not privatePractitioners and irsPractitioners on the case', () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '789', }; const result = runCompute(caseInformationHelper, { @@ -77,7 +77,7 @@ describe('case information helper', () => { it('should not show edit privatePractitioners or irsPractitioners buttons if user is not an internal user', () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '789', }; const result = runCompute(caseInformationHelper, { @@ -96,7 +96,7 @@ describe('case information helper', () => { it('should not show Seal Case button if user does not have SEAL_CASE permission', () => { const user = { - role: User.ROLES.petitionsClerk, // does not have SEAL_CASE permission + role: ROLES.petitionsClerk, // does not have SEAL_CASE permission userId: '789', }; const result = runCompute(caseInformationHelper, { @@ -111,7 +111,7 @@ describe('case information helper', () => { it('should show Seal Case button if user has SEAL_CASE permission and case is not already sealed', () => { const user = { - role: User.ROLES.docketClerk, // has SEAL_CASE permission + role: ROLES.docketClerk, // has SEAL_CASE permission userId: '789', }; const result = runCompute(caseInformationHelper, { @@ -126,7 +126,7 @@ describe('case information helper', () => { it('should not show Seal Case button if user has SEAL_CASE permission and case is already sealed', () => { const user = { - role: User.ROLES.docketClerk, // has SEAL_CASE permission + role: ROLES.docketClerk, // has SEAL_CASE permission userId: '789', }; const result = runCompute(caseInformationHelper, { diff --git a/web-client/src/presenter/computeds/caseInventoryReportHelper.test.js b/web-client/src/presenter/computeds/caseInventoryReportHelper.test.js index c51b02b9b99..b439e521d23 100644 --- a/web-client/src/presenter/computeds/caseInventoryReportHelper.test.js +++ b/web-client/src/presenter/computeds/caseInventoryReportHelper.test.js @@ -1,12 +1,15 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; + import { applicationContext } from '../../applicationContext'; import { caseInventoryReportHelper as caseInventoryReportHelperComputed } from './caseInventoryReportHelper'; import { runCompute } from 'cerebral/test'; import { withAppContextDecorator } from '../../withAppContext'; applicationContext.getCurrentUser = () => ({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '5d66d122-8417-427b-9048-c1ba8ab1ea68', }); @@ -32,7 +35,7 @@ describe('caseInventoryReportHelper', () => { }, }); - expect(result.caseStatuses).toEqual(Object.values(Case.STATUS_TYPES)); + expect(result.caseStatuses).toEqual(Object.values(CASE_STATUS_TYPES)); }); it('should return all judges from state along with Chief Judge sorted alphabetically', () => { @@ -101,15 +104,18 @@ describe('caseInventoryReportHelper', () => { caseInventoryReportData: { foundCases: [ { + correspondence: [], docketNumber: '123-20', docketNumberWithSuffix: '123-20', }, { + correspondence: [], docketNumber: '123-19', docketNumberSuffix: 'L', docketNumberWithSuffix: '123-19L', }, { + correspondence: [], docketNumber: '135-19', docketNumberWithSuffix: '135-19', }, @@ -252,7 +258,7 @@ describe('caseInventoryReportHelper', () => { const result = runCompute(caseInventoryReportHelper, { state: { caseInventoryReportData: { - foundCases: [{ docketNumber: '123-20' }], + foundCases: [{ correspondence: [], docketNumber: '123-20' }], totalCount: 1, }, screenMetadata: { diff --git a/web-client/src/presenter/computeds/caseSearchBoxHelper.test.js b/web-client/src/presenter/computeds/caseSearchBoxHelper.test.js index 0a0334d9418..7ca8135cfb1 100644 --- a/web-client/src/presenter/computeds/caseSearchBoxHelper.test.js +++ b/web-client/src/presenter/computeds/caseSearchBoxHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { caseSearchBoxHelper as caseSearchBoxHelperComputed } from './caseSearchBoxHelper'; import { runCompute } from 'cerebral/test'; @@ -12,7 +12,7 @@ const caseSearchBoxHelper = withAppContextDecorator( describe('caseSearchBoxHelper', () => { it('should return showSearchDescription true if the user role is not irsSuperuser', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, userId: '5d66d122-8417-427b-9048-c1ba8ab1ea68', }); @@ -25,7 +25,7 @@ describe('caseSearchBoxHelper', () => { it('should return showSearchDescription false if the user role is irsSuperuser', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.irsSuperuser, + role: ROLES.irsSuperuser, userId: '5d66d122-8417-427b-9048-c1ba8ab1ea68', }); diff --git a/web-client/src/presenter/computeds/completeDocumentTypeSectionHelper.test.js b/web-client/src/presenter/computeds/completeDocumentTypeSectionHelper.test.js index f2006c93f55..74854a0b6a6 100644 --- a/web-client/src/presenter/computeds/completeDocumentTypeSectionHelper.test.js +++ b/web-client/src/presenter/computeds/completeDocumentTypeSectionHelper.test.js @@ -1,4 +1,4 @@ -import { Document } from '../../../../shared/src/business/entities/Document'; +import { DOCUMENT_CATEGORY_MAP } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { completeDocumentTypeSectionHelper as completeDocumentTypeSectionHelperComputed } from './completeDocumentTypeSectionHelper'; import { runCompute } from 'cerebral/test'; @@ -25,7 +25,7 @@ describe('completeDocumentTypeSectionHelper', () => { const categoryKey = 'Application'; const categoryIdx = 0; - const { category, documentType } = Document.CATEGORY_MAP[categoryKey][ + const { category, documentType } = DOCUMENT_CATEGORY_MAP[categoryKey][ categoryIdx ]; @@ -49,7 +49,7 @@ describe('completeDocumentTypeSectionHelper', () => { const categoryKey = 'Motion'; const categoryIdx = 22; - const { category, documentType } = Document.CATEGORY_MAP[categoryKey][ + const { category, documentType } = DOCUMENT_CATEGORY_MAP[categoryKey][ categoryIdx ]; diff --git a/web-client/src/presenter/computeds/contactEditHelper.test.js b/web-client/src/presenter/computeds/contactEditHelper.test.js index 2dc9bfe23fa..8fa02475c08 100644 --- a/web-client/src/presenter/computeds/contactEditHelper.test.js +++ b/web-client/src/presenter/computeds/contactEditHelper.test.js @@ -1,5 +1,5 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; +import { PARTY_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { contactEditHelper as contactEditHelperComputed } from './contactEditHelper'; import { runCompute } from 'cerebral/test'; @@ -22,7 +22,7 @@ describe('contactEditHelper', () => { ...state, caseDetail: { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }; @@ -37,7 +37,7 @@ describe('contactEditHelper', () => { ...state, caseDetail: { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }, }; @@ -52,7 +52,7 @@ describe('contactEditHelper', () => { ...state, caseDetail: { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }, }; @@ -67,7 +67,7 @@ describe('contactEditHelper', () => { ...state, caseDetail: { ...MOCK_CASE, - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }, }; diff --git a/web-client/src/presenter/computeds/contactsHelper.test.js b/web-client/src/presenter/computeds/contactsHelper.test.js index fc6c41c6dc8..4a9b7153620 100644 --- a/web-client/src/presenter/computeds/contactsHelper.test.js +++ b/web-client/src/presenter/computeds/contactsHelper.test.js @@ -1,5 +1,7 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + PARTY_TYPES, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { contactsHelper as contactsHelperComputed } from './contactsHelper'; import { runCompute } from 'cerebral/test'; @@ -11,10 +13,10 @@ const contactsHelper = withAppContextDecorator( ); const petitionerUser = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, }; const practitionerUser = { - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }; describe('contactsHelper', () => { @@ -22,7 +24,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => petitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.conservator }, + form: { partyType: PARTY_TYPES.conservator }, }, }); expect(result).toMatchObject({ @@ -39,7 +41,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => petitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.corporation }, + form: { partyType: PARTY_TYPES.corporation }, }, }); expect(result).toMatchObject({ @@ -55,7 +57,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => petitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.custodian }, + form: { partyType: PARTY_TYPES.custodian }, }, }); expect(result).toMatchObject({ @@ -72,7 +74,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => petitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.donor }, + form: { partyType: PARTY_TYPES.donor }, }, }); expect(result).toMatchObject({ @@ -88,7 +90,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, }, }, }); @@ -109,7 +111,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }, }, }); @@ -127,7 +129,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }, }, }); @@ -146,7 +148,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + partyType: PARTY_TYPES.nextFriendForIncompetentPerson, }, }, }); @@ -166,7 +168,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, }, }, }); @@ -185,7 +187,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, }, }, }); @@ -204,7 +206,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + partyType: PARTY_TYPES.partnershipOtherThanTaxMatters, }, }, }); @@ -224,7 +226,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, }, }, }); @@ -243,7 +245,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -260,7 +262,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }, }, }); @@ -283,7 +285,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }, }, }); @@ -304,7 +306,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.survivingSpouse, + partyType: PARTY_TYPES.survivingSpouse, }, }, }); @@ -323,7 +325,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, }, }, }); @@ -340,7 +342,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, }, }, }); @@ -358,7 +360,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => practitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.conservator }, + form: { partyType: PARTY_TYPES.conservator }, }, }); expect(result).toMatchObject({ @@ -375,7 +377,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => practitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.corporation }, + form: { partyType: PARTY_TYPES.corporation }, }, }); expect(result).toMatchObject({ @@ -391,7 +393,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => practitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.custodian }, + form: { partyType: PARTY_TYPES.custodian }, }, }); expect(result).toMatchObject({ @@ -408,7 +410,7 @@ describe('contactsHelper', () => { applicationContext.getCurrentUser = () => practitionerUser; const result = runCompute(contactsHelper, { state: { - form: { partyType: ContactFactory.PARTY_TYPES.donor }, + form: { partyType: PARTY_TYPES.donor }, }, }); expect(result).toMatchObject({ @@ -424,7 +426,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, }, }, }); @@ -445,7 +447,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }, }, }); @@ -463,7 +465,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }, }, }); @@ -482,7 +484,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + partyType: PARTY_TYPES.nextFriendForIncompetentPerson, }, }, }); @@ -502,7 +504,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, }, }, }); @@ -521,7 +523,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, }, }, }); @@ -540,7 +542,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + partyType: PARTY_TYPES.partnershipOtherThanTaxMatters, }, }, }); @@ -559,7 +561,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, }, }, }); @@ -578,7 +580,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -595,7 +597,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }, }, }); @@ -618,7 +620,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }, }, }); @@ -639,7 +641,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.survivingSpouse, + partyType: PARTY_TYPES.survivingSpouse, }, }, }); @@ -658,7 +660,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, }, }, }); @@ -675,7 +677,7 @@ describe('contactsHelper', () => { const result = runCompute(contactsHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, }, }, }); diff --git a/web-client/src/presenter/computeds/createCaseMessageModalHelper.js b/web-client/src/presenter/computeds/createCaseMessageModalHelper.js new file mode 100644 index 00000000000..f4c70841b92 --- /dev/null +++ b/web-client/src/presenter/computeds/createCaseMessageModalHelper.js @@ -0,0 +1,34 @@ +import { state } from 'cerebral'; + +export const createCaseMessageModalHelper = (get, applicationContext) => { + const caseDetail = get(state.caseDetail); + const { + docketRecordWithDocument, + draftDocuments, + } = applicationContext + .getUtilities() + .getFormattedCaseDetail({ applicationContext, caseDetail }); + + const form = get(state.modal.form); + const screenMetadata = get(state.screenMetadata); + const documentAttachmentLimit = 5; // TODO: Do something better with this + + const currentAttachmentCount = (form.attachments || []).length; + const canAddDocument = currentAttachmentCount < documentAttachmentLimit; + const shouldShowAddDocumentForm = + currentAttachmentCount === 0 || screenMetadata.showAddDocumentForm; + + const documents = []; + docketRecordWithDocument.forEach(entry => { + if (entry.document) { + documents.push(entry.document); + } + }); + + return { + documents, + draftDocuments, + showAddDocumentForm: canAddDocument && shouldShowAddDocumentForm, + showAddMoreDocumentsButton: canAddDocument && !shouldShowAddDocumentForm, + }; +}; diff --git a/web-client/src/presenter/computeds/createCaseMessageModalHelper.test.js b/web-client/src/presenter/computeds/createCaseMessageModalHelper.test.js new file mode 100644 index 00000000000..9f4a6c4cf96 --- /dev/null +++ b/web-client/src/presenter/computeds/createCaseMessageModalHelper.test.js @@ -0,0 +1,176 @@ +import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; +import { applicationContext } from '../../applicationContext'; +import { createCaseMessageModalHelper as createCaseMessageModalHelperComputed } from './createCaseMessageModalHelper'; +import { runCompute } from 'cerebral/test'; +import { withAppContextDecorator } from '../../withAppContext'; + +const createCaseMessageModalHelper = withAppContextDecorator( + createCaseMessageModalHelperComputed, + applicationContext, +); + +describe('createCaseMessageModalHelper', () => { + let caseDetail; + + beforeAll(() => { + applicationContext.getCurrentUser = () => ({ + userId: MOCK_CASE.userId, + }); + + caseDetail = { + ...MOCK_CASE, + docketRecord: [ + { documentId: '123', index: 1 }, + { documentId: '234', index: 2 }, + { index: 3 }, // note: no document + ], + documents: [ + { documentId: '123', documentType: 'Petition' }, + { documentId: '234', documentTitle: 'Some Document' }, + { documentId: '345', documentType: 'Order' }, + ], + }; + }); + + it('returns documents on the docket record', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: {}, + }, + screenMetadata: { + showAddDocumentForm: true, + }, + }, + }); + + expect(result.documents).toMatchObject([ + { documentId: '123' }, + { documentId: '234' }, + ]); + expect(result.documents.length).toEqual(2); + }); + + it('returns draftDocuments from formattedCaseDetail', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: {}, + }, + screenMetadata: { + showAddDocumentForm: true, + }, + }, + }); + + expect(result.draftDocuments).toMatchObject([{ documentId: '345' }]); + }); + + it('returns showAddDocumentForm true when the current attachment count is zero', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: {}, + }, + screenMetadata: { + showAddDocumentForm: true, + }, + }, + }); + + expect(result.showAddDocumentForm).toEqual(true); + }); + + it('returns showAddDocumentForm true when screenMetadata.showAddDocumentForm is true and the maximum number of attachments has not been met', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: { + attachments: [{}, {}, {}, {}], // 4/5 documents attached + }, + }, + screenMetadata: { + showAddDocumentForm: true, + }, + }, + }); + + expect(result.showAddDocumentForm).toEqual(true); + }); + + it('returns showAddDocumentForm false when screenMetadata.showAddDocumentForm is false and the maximum number of attachments has not been met', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: { + attachments: [{}, {}, {}, {}], // 4/5 documents attached + }, + }, + screenMetadata: { + showAddDocumentForm: false, + }, + }, + }); + + expect(result.showAddDocumentForm).toEqual(false); + }); + + it('returns showAddDocumentForm false when maximum number of attachments have been reached', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: { + attachments: [{}, {}, {}, {}, {}], // 5/5 documents attached + }, + }, + screenMetadata: { + showAddDocumentForm: true, + }, + }, + }); + + expect(result.showAddDocumentForm).toEqual(false); + }); + + it('returns showAddMoreDocumentsButton true when showAddDocumentForm is false and the current attachment count is greater than zero but less than the maximum', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: { + attachments: [{}, {}, {}, {}], // 4/5 documents attached + }, + }, + screenMetadata: { + showAddDocumentForm: false, + }, + }, + }); + + expect(result.showAddMoreDocumentsButton).toEqual(true); + }); + + it('returns showAddMoreDocumentsButton false when maximum number of attachments have been reached', () => { + const result = runCompute(createCaseMessageModalHelper, { + state: { + caseDetail, + modal: { + form: { + attachments: [{}, {}, {}, {}, {}], // 5/5 documents attached + }, + }, + screenMetadata: { + showAddDocumentForm: false, + }, + }, + }); + + expect(result.showAddMoreDocumentsButton).toEqual(false); + }); +}); diff --git a/web-client/src/presenter/computeds/dashboardExternalHelper.js b/web-client/src/presenter/computeds/dashboardExternalHelper.js index 27fcfc426ac..e0091b44235 100644 --- a/web-client/src/presenter/computeds/dashboardExternalHelper.js +++ b/web-client/src/presenter/computeds/dashboardExternalHelper.js @@ -1,7 +1,9 @@ import { state } from 'cerebral'; export const dashboardExternalHelper = (get, applicationContext) => { - const cases = get(state.cases); + const openCases = get(state.openCases) || []; + const closedCases = get(state.closedCases) || []; + const cases = [...openCases, ...closedCases]; const user = applicationContext.getCurrentUser() || {}; return { diff --git a/web-client/src/presenter/computeds/dashboardExternalHelper.test.js b/web-client/src/presenter/computeds/dashboardExternalHelper.test.js index 8a2456d9565..77b59d1fc17 100644 --- a/web-client/src/presenter/computeds/dashboardExternalHelper.test.js +++ b/web-client/src/presenter/computeds/dashboardExternalHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { dashboardExternalHelper as dashboardExternalHelperComputed } from './dashboardExternalHelper'; import { runCompute } from 'cerebral/test'; @@ -10,26 +10,28 @@ const dashboardExternalHelper = withAppContextDecorator( ); describe('petitioner dashboard helper', () => { - it('shows "what to expect" but not case list when there are no cases', () => { + it('shows "what to expect" but not case list when there are no open or closed cases', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); const result = runCompute(dashboardExternalHelper, { state: { - cases: [], + closedCases: [], + openCases: [], }, }); expect(result.showCaseList).toEqual(false); expect(result.showWhatToExpect).toEqual(true); expect(result.showCaseSearch).toEqual(false); }); - it('shows case list but not "what to expect" when there is a case', () => { + it('shows case list but not "what to expect" when there is an open or closed case case', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); const result = runCompute(dashboardExternalHelper, { state: { - cases: [{ something: true }], + closedCases: [{ something: true }], + openCases: [{ something: true }], }, }); expect(result.showCaseList).toEqual(true); @@ -38,11 +40,12 @@ describe('petitioner dashboard helper', () => { }); it('shows case search if defined user has privatePractitioner role', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); const result = runCompute(dashboardExternalHelper, { state: { - cases: [{ something: true }], + closedCases: [{ something: true }], + openCases: [{ something: true }], }, }); expect(result.showCaseList).toEqual(true); @@ -52,11 +55,12 @@ describe('petitioner dashboard helper', () => { it('shows case search if defined user has irsPractitioner role', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }); const result = runCompute(dashboardExternalHelper, { state: { - cases: [{ something: true }], + closedCases: [{ something: true }], + openCases: [{ something: true }], }, }); expect(result.showCaseList).toEqual(true); @@ -66,11 +70,12 @@ describe('petitioner dashboard helper', () => { it('hides case search if defined user does not have privatePractitioner or irsPractitioner role', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }); const result = runCompute(dashboardExternalHelper, { state: { - cases: [{ something: true }], + closedCases: [{ something: true }], + openCases: [{ something: true }], }, }); expect(result.showCaseList).toEqual(true); diff --git a/web-client/src/presenter/computeds/documentDetailHelper.js b/web-client/src/presenter/computeds/documentDetailHelper.js index 7735fe5702a..f674ba352bc 100644 --- a/web-client/src/presenter/computeds/documentDetailHelper.js +++ b/web-client/src/presenter/computeds/documentDetailHelper.js @@ -49,9 +49,15 @@ export const documentDetailHelper = (get, applicationContext) => { const caseDetail = get(state.caseDetail); const permissions = get(state.permissions); const documentId = get(state.documentId); - const document = caseDetail.documents.find( + + const allCaseDocuments = [ + ...(caseDetail.documents || []), + ...(caseDetail.correspondence || []), + ]; + const document = allCaseDocuments.find( item => item.documentId === documentId, ); + if (!document) { return; } diff --git a/web-client/src/presenter/computeds/documentDetailHelper.test.js b/web-client/src/presenter/computeds/documentDetailHelper.test.js index 856f4cff3db..548426cdca2 100644 --- a/web-client/src/presenter/computeds/documentDetailHelper.test.js +++ b/web-client/src/presenter/computeds/documentDetailHelper.test.js @@ -1,5 +1,8 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; +import { Correspondence } from '../../../../shared/src/business/entities/Correspondence'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { documentDetailHelper as documentDetailHelperComputed, @@ -26,7 +29,7 @@ const documentDetailHelper = withAppContextDecorator( const getBaseState = user => { globalUser = user; return { - constants: { STATUS_TYPES: Case.STATUS_TYPES, USER_ROLES: User.ROLES }, + constants: { STATUS_TYPES: CASE_STATUS_TYPES, USER_ROLES: ROLES }, permissions: getUserPermissions(user), }; }; @@ -35,13 +38,13 @@ describe('document detail helper', () => { describe('formatDocumentWorkItems', () => { it('should return filtered and formatted completedWorkItems, incompleteWorkItems, and qcWorkItem', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const workItems = [ { assigneeId: user.userId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, completedAt: '2018-12-21T20:49:28.192Z', createdAt: '2018-11-21T20:49:28.192Z', document: { @@ -59,7 +62,7 @@ describe('document detail helper', () => { }, { assigneeId: user.userId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, createdAt: '2018-11-22T20:49:28.192Z', document: { documentId: 'abc', @@ -76,7 +79,7 @@ describe('document detail helper', () => { }, { assigneeId: user.userId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, createdAt: '2018-11-23T20:49:28.192Z', document: { documentId: 'abc', @@ -116,7 +119,7 @@ describe('document detail helper', () => { it('showAction function should return true for complete for document Id abc', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -125,7 +128,7 @@ describe('document detail helper', () => { caseDetail: { docketRecord: [], documents: [{ documentId: 'abc' }], - status: Case.STATUS_TYPES.generalDocket, + status: CASE_STATUS_TYPES.generalDocket, }, documentId: 'abc', workItemActions: { @@ -138,7 +141,7 @@ describe('document detail helper', () => { it('should set showSignDocumentButton to true when user has COURT_ISSUED_DOCUMENT permission and there is a valid document to sign that is not already signed', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -153,7 +156,7 @@ describe('document detail helper', () => { workItems: [ { assigneeId: user.userId, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, document: { documentId: 'abc', documentType: 'Proposed Stipulated Decision', @@ -169,7 +172,7 @@ describe('document detail helper', () => { ], }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { @@ -183,7 +186,7 @@ describe('document detail helper', () => { describe('createdFiledLabel', () => { it('should set createFiledLabel to `Created` for a court-issued document', async () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; @@ -198,7 +201,7 @@ describe('document detail helper', () => { documentType: 'Order of Dismissal', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', workItemActions: { @@ -211,7 +214,7 @@ describe('document detail helper', () => { it('should set createFiledLabel to `Filed` for a non court-issued document', async () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; @@ -226,7 +229,7 @@ describe('document detail helper', () => { documentType: 'Petition', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', workItemActions: { @@ -241,7 +244,7 @@ describe('document detail helper', () => { describe('showCreatedFiled', () => { it('should set showCreatedFiled to true if the document is not an order or court-issued document', async () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; @@ -256,7 +259,7 @@ describe('document detail helper', () => { documentType: 'Answer', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', workItemActions: { @@ -269,7 +272,7 @@ describe('document detail helper', () => { it('should set showCreatedFiled to true if the document is an order and is in draft state (not on the docket record)', async () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; @@ -284,7 +287,7 @@ describe('document detail helper', () => { documentType: 'Order', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', workItemActions: { @@ -297,7 +300,7 @@ describe('document detail helper', () => { it('should set showCreatedFiled to false if the document is an order and is in not draft state (on the docket record)', async () => { const user = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '123', }; @@ -312,7 +315,7 @@ describe('document detail helper', () => { documentType: 'Order', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', workItemActions: { @@ -327,7 +330,7 @@ describe('document detail helper', () => { describe('showAddCourtIssuedDocketEntryButton', () => { it('should set showAddCourtIssuedDocketEntryButton true when the user has the DOCKET_ENTRY permission and the document is an unsigned stipulated decision', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -342,7 +345,7 @@ describe('document detail helper', () => { documentType: 'Stipulated Decision', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -356,7 +359,7 @@ describe('document detail helper', () => { it('should set showAddCourtIssuedDocketEntryButton true when the user has the DOCKET_ENTRY permission and the document is a signed stipulated decision', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -372,7 +375,7 @@ describe('document detail helper', () => { signedAt: getDateISO(), }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -386,7 +389,7 @@ describe('document detail helper', () => { it('should set showAddCourtIssuedDocketEntryButton true when the user has the DOCKET_ENTRY permission and the document is an unsigned order', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -401,7 +404,7 @@ describe('document detail helper', () => { documentType: 'Order of Dismissal', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -415,7 +418,7 @@ describe('document detail helper', () => { it('should set showAddCourtIssuedDocketEntryButton false when the user has the DOCKET_ENTRY permission and the document is a served order', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -431,7 +434,7 @@ describe('document detail helper', () => { servedAt: getDateISO(), }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -445,7 +448,7 @@ describe('document detail helper', () => { it('should set showAddCourtIssuedDocketEntryButton false when the user does not have the DOCKET_ENTRY permission', async () => { const user = { - role: User.ROLES.petitioner, + role: ROLES.petitioner, userId: '123', }; @@ -460,7 +463,7 @@ describe('document detail helper', () => { documentType: 'Stipulated Decision', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: false }, @@ -474,7 +477,7 @@ describe('document detail helper', () => { it('should set showAddCourtIssuedDocketEntryButton false when the document type is not an order or stipulated decision', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -489,7 +492,7 @@ describe('document detail helper', () => { documentType: 'Petition', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -505,7 +508,7 @@ describe('document detail helper', () => { describe('showEditDocketEntry and showEditCourtIssuedDocketEntry', () => { it('should set showEditDocketEntry false and showEditCourtIssuedDocketEntry true when the document is a signed stipulated decision with a docket entry and the user has the DOCKET_ENTRY permission', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -525,7 +528,7 @@ describe('document detail helper', () => { signedAt: getDateISO(), }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -540,7 +543,7 @@ describe('document detail helper', () => { it('should set showEditDocketEntry false when the document is an unsigned stipulated decision and the user has the DOCKET_ENTRY permission', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -555,7 +558,7 @@ describe('document detail helper', () => { documentType: 'Stipulated Decision', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -569,7 +572,7 @@ describe('document detail helper', () => { it('should set showEditDocketEntry true when the non QCed document is a served order and the user has the DOCKET_ENTRY permission', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -585,7 +588,7 @@ describe('document detail helper', () => { servedAt: getDateISO(), workItems: [ { - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, document: { receivedAt: '2018-11-21T20:49:28.192Z', }, @@ -604,7 +607,7 @@ describe('document detail helper', () => { ], }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -618,7 +621,7 @@ describe('document detail helper', () => { it('should set showEditDocketEntry false on a QCed document even when it is a served order and the user has the DOCKET_ENTRY permission', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -634,7 +637,7 @@ describe('document detail helper', () => { servedAt: getDateISO(), workItems: [ { - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, completedAt: '2018-11-21T20:49:28.192Z', document: { receivedAt: '2018-11-21T20:49:28.192Z', @@ -654,7 +657,7 @@ describe('document detail helper', () => { ], }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -668,7 +671,7 @@ describe('document detail helper', () => { it('should set showEditDocketEntry false when the document is an unserved order and the user has the DOCKET_ENTRY permission', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -683,7 +686,7 @@ describe('document detail helper', () => { documentType: 'Order of Dismissal', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -697,7 +700,7 @@ describe('document detail helper', () => { it('should set showEditDocketEntry false when the document is petition and the user has the DOCKET_ENTRY permission', async () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; @@ -712,7 +715,7 @@ describe('document detail helper', () => { documentType: 'Petition', }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }, documentId: 'abc', permissions: { DOCKET_ENTRY: true }, @@ -725,253 +728,409 @@ describe('document detail helper', () => { }); }); - it('should indicate QC completed by workItem "completedBy" if not indicated on Document', () => { - const user = { - role: User.ROLES.petitionsClerk, - userId: '123', - }; - const result = runCompute(documentDetailHelper, { - state: { - ...getBaseState(user), - caseDetail: { - docketRecord: [], - documents: [ - { - createdAt: '2018-11-21T20:49:28.192Z', - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - documentType: 'Proposed Stipulated Decision', - processingStatus: 'pending', - reviewDate: '2018-11-21T20:49:28.192Z', - userId: 'petitioner', - workItems: [ - { - caseStatus: Case.STATUS_TYPES.new, - completedAt: '2018-11-21T20:49:28.192Z', - completedBy: 'William T. Riker', - document: { - receivedAt: '2018-11-21T20:49:28.192Z', - }, - messages: [ - { - createdAt: '2018-11-21T20:49:28.192Z', - - message: 'Served on IRS', - }, - { - createdAt: '2018-11-21T20:49:28.192Z', - message: 'Test', + describe('formattedDocument', () => { + it('should search for the specified document in the case correspondence list', () => { + const user = { + role: ROLES.petitionsClerk, + userId: '123', + }; + const mockCorrespondence = new Correspondence({ + documentId: '123-abc', + documentTitle: 'My Correspondence', + filedBy: 'Docket clerk', + }); + const result = runCompute(documentDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { + correspondence: [mockCorrespondence], + docketRecord: [], + documents: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Proposed Stipulated Decision', + processingStatus: 'pending', + reviewDate: '2018-11-21T20:49:28.192Z', + userId: 'petitioner', + workItems: [ + { + caseStatus: CASE_STATUS_TYPES.new, + completedAt: '2018-11-21T20:49:28.192Z', + completedBy: 'William T. Riker', + document: { + receivedAt: '2018-11-21T20:49:28.192Z', }, - ], - }, - { - assigneeId: 'abc', - caseStatus: Case.STATUS_TYPES.new, - document: { - documentType: 'Proposed Stipulated Decision', - receivedAt: '2018-11-21T20:49:28.192Z', - }, - messages: [ - { - createdAt: '2018-11-21T20:49:28.192Z', + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', - message: 'Served on IRS', - }, - { - createdAt: '2018-11-21T20:49:28.192Z', - message: 'Test', + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + { + assigneeId: 'abc', + caseStatus: CASE_STATUS_TYPES.new, + document: { + documentType: 'Proposed Stipulated Decision', + receivedAt: '2018-11-21T20:49:28.192Z', }, - ], - }, - ], - }, - ], + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + ], + }, + ], + }, + documentId: '123-abc', + workQueueToDisplay: { workQueueIsInternal: true }, }, - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - workQueueToDisplay: { workQueueIsInternal: true }, - }, - }); + }); - expect(result.formattedDocument.qcInfo).toMatchObject({ - date: '11/21/18', - name: 'William T. Riker', + expect(result.formattedDocument.documentId).toEqual( + mockCorrespondence.documentId, + ); }); - }); - it('should indicate QC completed by "qcByUser" on Document if present', () => { - const user = { - role: User.ROLES.petitionsClerk, - userId: '123', - }; - const result = runCompute(documentDetailHelper, { - state: { - ...getBaseState(user), - caseDetail: { - docketRecord: [], - documents: [ - { - createdAt: '2018-11-21T20:49:28.192Z', - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + it('should search for the specified document in the case documents list', () => { + const user = { + role: ROLES.petitionsClerk, + userId: '123', + }; + const mockCorrespondence = new Correspondence({ + documentId: '123-abc', + documentTitle: 'My Correspondence', + filedBy: 'Docket clerk', + }); + const mockDocument = { + createdAt: '2018-11-21T20:49:28.192Z', + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Proposed Stipulated Decision', + processingStatus: 'pending', + reviewDate: '2018-11-21T20:49:28.192Z', + userId: 'petitioner', + workItems: [ + { + caseStatus: CASE_STATUS_TYPES.new, + completedAt: '2018-11-21T20:49:28.192Z', + completedBy: 'William T. Riker', + document: { + receivedAt: '2018-11-21T20:49:28.192Z', + }, + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + { + assigneeId: 'abc', + caseStatus: CASE_STATUS_TYPES.new, + document: { documentType: 'Proposed Stipulated Decision', - processingStatus: 'pending', - qcAt: '2019-10-27T20:49:28.192Z', - qcByUser: { - name: 'Reginald Barclay', - userId: 'xyzzy', + receivedAt: '2018-11-21T20:49:28.192Z', + }, + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', }, - reviewDate: '2018-11-21T20:49:28.192Z', - userId: 'petitioner', - workItems: [ - { - caseStatus: Case.STATUS_TYPES.new, - completedAt: '2018-11-21T20:49:28.192Z', - completedBy: 'William T. Riker', - document: { - receivedAt: '2018-11-21T20:49:28.192Z', - }, - messages: [ - { - createdAt: '2018-11-21T20:49:28.192Z', + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + ], + }; - message: 'Served on IRS', - }, - { - createdAt: '2018-11-21T20:49:28.192Z', - message: 'Test', - }, - ], - }, - { - assigneeId: 'abc', - caseStatus: Case.STATUS_TYPES.new, - document: { - documentType: 'Proposed Stipulated Decision', - receivedAt: '2018-11-21T20:49:28.192Z', - }, - messages: [ - { - createdAt: '2018-11-21T20:49:28.192Z', + const result = runCompute(documentDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { + correspondence: [mockCorrespondence], + docketRecord: [], + documents: [mockDocument], + }, + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + workQueueToDisplay: { workQueueIsInternal: true }, + }, + }); + + expect(result.formattedDocument.documentId).toEqual( + mockDocument.documentId, + ); + }); - message: 'Served on IRS', + it('should indicate QC completed by workItem "completedBy" if not indicated on Document', () => { + const user = { + role: ROLES.petitionsClerk, + userId: '123', + }; + const result = runCompute(documentDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { + docketRecord: [], + documents: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Proposed Stipulated Decision', + processingStatus: 'pending', + reviewDate: '2018-11-21T20:49:28.192Z', + userId: 'petitioner', + workItems: [ + { + caseStatus: CASE_STATUS_TYPES.new, + completedAt: '2018-11-21T20:49:28.192Z', + completedBy: 'William T. Riker', + document: { + receivedAt: '2018-11-21T20:49:28.192Z', }, - { - createdAt: '2018-11-21T20:49:28.192Z', - message: 'Test', + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + { + assigneeId: 'abc', + caseStatus: CASE_STATUS_TYPES.new, + document: { + documentType: 'Proposed Stipulated Decision', + receivedAt: '2018-11-21T20:49:28.192Z', }, - ], - }, - ], - }, - ], + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + ], + }, + ], + }, + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + workQueueToDisplay: { workQueueIsInternal: true }, }, - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - workQueueToDisplay: { workQueueIsInternal: true }, - }, - }); + }); - expect(result.formattedDocument.qcInfo).toMatchObject({ - date: '10/27/19', - name: 'Reginald Barclay', + expect(result.formattedDocument.qcInfo).toMatchObject({ + date: '11/21/18', + name: 'William T. Riker', + }); }); - }); - - it('should filter out completed work items with Served on IRS messages', () => { - const user = { - role: User.ROLES.adc, - userId: '123', - }; - const result = runCompute(documentDetailHelper, { - state: { - ...getBaseState(user), - caseDetail: { - docketRecord: [], - documents: [ - { - createdAt: '2018-11-21T20:49:28.192Z', - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - documentType: 'Proposed Stipulated Decision', - processingStatus: 'pending', - reviewDate: '2018-11-21T20:49:28.192Z', - userId: 'petitioner', - workItems: [ - { - caseStatus: Case.STATUS_TYPES.new, - completedAt: '2018-11-21T20:49:28.192Z', - document: { - receivedAt: '2018-11-21T20:49:28.192Z', - }, - messages: [ - { - createdAt: '2018-11-21T20:49:28.192Z', - message: 'Served on IRS', + it('should indicate QC completed by "qcByUser" on Document if present', () => { + const user = { + role: ROLES.petitionsClerk, + userId: '123', + }; + const result = runCompute(documentDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { + docketRecord: [], + documents: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Proposed Stipulated Decision', + processingStatus: 'pending', + qcAt: '2019-10-27T20:49:28.192Z', + qcByUser: { + name: 'Reginald Barclay', + userId: 'xyzzy', + }, + reviewDate: '2018-11-21T20:49:28.192Z', + userId: 'petitioner', + workItems: [ + { + caseStatus: CASE_STATUS_TYPES.new, + completedAt: '2018-11-21T20:49:28.192Z', + completedBy: 'William T. Riker', + document: { + receivedAt: '2018-11-21T20:49:28.192Z', }, - { - createdAt: '2018-11-21T20:49:28.192Z', - message: 'Test', + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + { + assigneeId: 'abc', + caseStatus: CASE_STATUS_TYPES.new, + document: { + documentType: 'Proposed Stipulated Decision', + receivedAt: '2018-11-21T20:49:28.192Z', }, - ], - }, - { - assigneeId: 'abc', - caseStatus: Case.STATUS_TYPES.new, - document: { - documentType: 'Proposed Stipulated Decision', - receivedAt: '2018-11-21T20:49:28.192Z', + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], }, - messages: [ - { - createdAt: '2018-11-21T20:49:28.192Z', + ], + }, + ], + }, + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + workQueueToDisplay: { workQueueIsInternal: true }, + }, + }); - message: 'Served on IRS', + expect(result.formattedDocument.qcInfo).toMatchObject({ + date: '10/27/19', + name: 'Reginald Barclay', + }); + }); + + it('should filter out completed work items with Served on IRS messages', () => { + const user = { + role: ROLES.adc, + userId: '123', + }; + const result = runCompute(documentDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { + docketRecord: [], + documents: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + documentType: 'Proposed Stipulated Decision', + processingStatus: 'pending', + reviewDate: '2018-11-21T20:49:28.192Z', + userId: 'petitioner', + workItems: [ + { + caseStatus: CASE_STATUS_TYPES.new, + completedAt: '2018-11-21T20:49:28.192Z', + document: { + receivedAt: '2018-11-21T20:49:28.192Z', }, - { - createdAt: '2018-11-21T20:49:28.192Z', - message: 'Test', + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + { + assigneeId: 'abc', + caseStatus: CASE_STATUS_TYPES.new, + document: { + documentType: 'Proposed Stipulated Decision', + receivedAt: '2018-11-21T20:49:28.192Z', }, - ], - }, - ], - }, - ], + messages: [ + { + createdAt: '2018-11-21T20:49:28.192Z', + + message: 'Served on IRS', + }, + { + createdAt: '2018-11-21T20:49:28.192Z', + message: 'Test', + }, + ], + }, + ], + }, + ], + }, + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + workQueueToDisplay: { workQueueIsInternal: true }, }, - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - workQueueToDisplay: { workQueueIsInternal: true }, - }, - }); + }); - expect(result.formattedDocument.workItems).toHaveLength(1); - }); + expect(result.formattedDocument.workItems).toHaveLength(1); + }); - it("default to empty array when a document's workItems are non-existent", () => { - const user = { - role: User.ROLES.petitionsClerk, - userId: '123', - }; - const result = runCompute(documentDetailHelper, { - state: { - ...getBaseState(user), - caseDetail: { - docketRecord: [], - documents: [ - { - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - }, - ], + it("default to empty array when a document's workItems are non-existent", () => { + const user = { + role: ROLES.petitionsClerk, + userId: '123', + }; + const result = runCompute(documentDetailHelper, { + state: { + ...getBaseState(user), + caseDetail: { + docketRecord: [], + documents: [ + { + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + }, + ], + }, + documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', }, - documentId: 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - }, - }); + }); - expect(result.formattedDocument.documentId).toEqual( - 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', - ); - expect(result.formattedDocument.workItems).toEqual([]); + expect(result.formattedDocument.documentId).toEqual( + 'def81f4d-1e47-423a-8caf-6d2fdc3d3859', + ); + expect(result.formattedDocument.workItems).toEqual([]); + }); }); describe('showConfirmEditOrder, showSignedAt, and showRemoveSignature', () => { it('should show confirm edit order, signed at, and remove signature for a signed order', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -989,7 +1148,7 @@ describe('document detail helper', () => { }, documentId: '123-abc', user: { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }, }, }); @@ -1001,7 +1160,7 @@ describe('document detail helper', () => { it('should show confirm edit order, signed at, but NOT remove signature for a signed notice', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1020,7 +1179,7 @@ describe('document detail helper', () => { }, documentId: '123-abc', user: { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }, }, }); @@ -1032,7 +1191,7 @@ describe('document detail helper', () => { it('should NOT show confirm edit order OR remove signature when the documentType is not an order', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1050,7 +1209,7 @@ describe('document detail helper', () => { }, documentId: '123-abc', user: { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }, }, }); @@ -1062,7 +1221,7 @@ describe('document detail helper', () => { it('should NOT show confirm edit order OR remove signature when the document has not been signed', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1080,7 +1239,7 @@ describe('document detail helper', () => { }, documentId: '123-abc', user: { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }, }, }); @@ -1094,7 +1253,7 @@ describe('document detail helper', () => { describe('showPrintCaseConfirmationButton', () => { it("should show the 'Print Confirmation' button if a document has been served and the document is a petition ", () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1120,7 +1279,7 @@ describe('document detail helper', () => { it("should not show the 'Print Confirmation' button if a document has not been served", () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1146,7 +1305,7 @@ describe('document detail helper', () => { it("should not show the 'Print Confirmation' button if the document is not a petition ", () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1174,7 +1333,7 @@ describe('document detail helper', () => { describe('isDraftDocument', () => { it('should return isDraftDocument false if the document is served', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1200,7 +1359,7 @@ describe('document detail helper', () => { it('should return isDraftDocument true if the document is an unserved Stipulated Decision', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1225,7 +1384,7 @@ describe('document detail helper', () => { it('should return isDraftDocument true if the document is an order that is NOT on the docket record', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1250,7 +1409,7 @@ describe('document detail helper', () => { it('should return isDraftDocument false if the document is an order that is on the docket record', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1279,7 +1438,7 @@ describe('document detail helper', () => { it('should return isDraftDocument true if the document is a court-issued document that is NOT on the docket record', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1304,7 +1463,7 @@ describe('document detail helper', () => { it('should return isDraftDocument false if the document is a court-issued document that is on the docket record', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1329,7 +1488,7 @@ describe('document detail helper', () => { it('should return isDraftDocument false if the document is unserved but is not an internal document type', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1355,7 +1514,7 @@ describe('document detail helper', () => { describe('editUrl', () => { it('should go to the sign url when the document is a stip decision', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1383,7 +1542,7 @@ describe('document detail helper', () => { it('should go to the edit upload pdf url when the document is a Miscellaneous document', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { @@ -1411,7 +1570,7 @@ describe('document detail helper', () => { it('should go to the edit order url when the document is a Order document', () => { const user = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const result = runCompute(documentDetailHelper, { diff --git a/web-client/src/presenter/computeds/editStatisticFormHelper.js b/web-client/src/presenter/computeds/editStatisticFormHelper.js new file mode 100644 index 00000000000..7be46b13821 --- /dev/null +++ b/web-client/src/presenter/computeds/editStatisticFormHelper.js @@ -0,0 +1,37 @@ +import { state } from 'cerebral'; + +/** + * gets the edit statistic form helper fields + * + * @param {Function} get the cerebral get function + * @param {object} applicationContext the application context + * @returns {object} edit statistic form helper fields + */ +export const editStatisticFormHelper = (get, applicationContext) => { + const { CASE_TYPES_MAP } = applicationContext.getConstants(); + const caseDetail = get(state.caseDetail); + const form = get(state.form); + const statisticsRequired = + caseDetail.caseType === CASE_TYPES_MAP.deficiency && + caseDetail.hasVerifiedIrsNotice === true; + + let showDelete = true; + if ( + statisticsRequired && + caseDetail.statistics && + caseDetail.statistics.length === 1 + ) { + showDelete = false; + } + + const headerDateDisplay = + form.year || + applicationContext + .getUtilities() + .formatDateString(form.lastDateOfPeriod, 'MMDDYY'); + + return { + headerDateDisplay, + showDelete, + }; +}; diff --git a/web-client/src/presenter/computeds/editStatisticFormHelper.test.js b/web-client/src/presenter/computeds/editStatisticFormHelper.test.js new file mode 100644 index 00000000000..8634c4688ef --- /dev/null +++ b/web-client/src/presenter/computeds/editStatisticFormHelper.test.js @@ -0,0 +1,89 @@ +import { CASE_TYPES_MAP } from '../../../../shared/src/business/entities/EntityConstants'; +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { editStatisticFormHelper as editStatisticFormHelperComputed } from './editStatisticFormHelper'; +import { runCompute } from 'cerebral/test'; +import { withAppContextDecorator } from '../../withAppContext'; + +const editStatisticFormHelper = withAppContextDecorator( + editStatisticFormHelperComputed, + applicationContext, +); + +describe('editStatisticFormHelper', () => { + it('sets headerDateDisplay to state.form.year if present', () => { + const result = runCompute(editStatisticFormHelper, { + state: { + caseDetail: {}, + form: { + year: '2012', + }, + }, + }); + expect(result.headerDateDisplay).toEqual('2012'); + }); + + it('sets headerDateDisplay to formatted state.form.lastDateOfPeriod if year is not present', () => { + const result = runCompute(editStatisticFormHelper, { + state: { + caseDetail: {}, + form: { + lastDateOfPeriod: '2019-03-01T21:40:46.415Z', + }, + }, + }); + expect(result.headerDateDisplay).toEqual('03/01/19'); + }); + + it('sets showDelete true if the case type is not deficiency', () => { + const result = runCompute(editStatisticFormHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.cdp, + }, + form: {}, + }, + }); + expect(result.showDelete).toEqual(true); + }); + + it('sets showDelete true if the case type is deficiency and hasVerifiedIrsNotice is false', () => { + const result = runCompute(editStatisticFormHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: false, + }, + form: {}, + }, + }); + expect(result.showDelete).toEqual(true); + }); + + it('sets showDelete true if the case type is deficiency and hasVerifiedIrsNotice is true and statistics length is greater than 1', () => { + const result = runCompute(editStatisticFormHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: true, + statistics: [{ irsTotalPenalties: 1 }, { irsTotalPenalties: 2 }], + }, + form: {}, + }, + }); + expect(result.showDelete).toEqual(true); + }); + + it('sets showDelete false if the case type is deficiency and hasVerifiedIrsNotice is true and statistics length is 1', () => { + const result = runCompute(editStatisticFormHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.deficiency, + hasVerifiedIrsNotice: true, + statistics: [{ irsTotalPenalties: 1 }], + }, + form: {}, + }, + }); + expect(result.showDelete).toEqual(false); + }); +}); diff --git a/web-client/src/presenter/computeds/extractDocument.js b/web-client/src/presenter/computeds/extractDocument.js deleted file mode 100644 index 7b40f28c60f..00000000000 --- a/web-client/src/presenter/computeds/extractDocument.js +++ /dev/null @@ -1,23 +0,0 @@ -import { formatWorkItem } from './formattedWorkQueue'; -import { state } from 'cerebral'; - -export const extractedDocument = (get, applicationContext) => { - const caseDetail = get(state.caseDetail); - const documentId = get(state.documentId); - const selectedDocument = (caseDetail.documents || []).find( - document => document.documentId === documentId, - ); - if (!selectedDocument) return {}; - const formattedDocument = applicationContext - .getUtilities() - .formatDocument(applicationContext, selectedDocument); - formattedDocument.workItems = (formattedDocument.workItems || []) - .filter(items => !items.completedAt) - .map(items => - formatWorkItem({ - applicationContext, - workItem: items, - }), - ); - return formattedDocument; -}; diff --git a/web-client/src/presenter/computeds/extractDocument.test.js b/web-client/src/presenter/computeds/extractDocument.test.js deleted file mode 100644 index 540b2e11c67..00000000000 --- a/web-client/src/presenter/computeds/extractDocument.test.js +++ /dev/null @@ -1,46 +0,0 @@ -import { runCompute } from 'cerebral/test'; - -import { extractedDocument as extractedDocumentComputed } from './extractDocument'; -import { withAppContextDecorator } from '../../../src/withAppContext'; - -const extractedDocument = withAppContextDecorator(extractedDocumentComputed); - -describe('extractedDocument', () => { - it('should return an empty object if no document was found', () => { - const result = runCompute(extractedDocument, { - state: { - caseDetail: { - documents: [], - }, - documentId: 'abc', - }, - }); - expect(result).toMatchObject({}); - }); - - it('should return an empty object if no document was found', () => { - const result = runCompute(extractedDocument, { - state: { - caseDetail: {}, - documentId: 'abc', - }, - }); - expect(result).toMatchObject({}); - }); - - it('should not fail when workItems is undefined', () => { - const result = runCompute(extractedDocument, { - state: { - caseDetail: { - documents: [ - { - documentId: 'abc', - }, - ], - }, - documentId: 'abc', - }, - }); - expect(result).toMatchObject({}); - }); -}); diff --git a/web-client/src/presenter/computeds/extractPendingMessagesFromCaseDetail.test.js b/web-client/src/presenter/computeds/extractPendingMessagesFromCaseDetail.test.js index c2395ade77e..02a7766d191 100644 --- a/web-client/src/presenter/computeds/extractPendingMessagesFromCaseDetail.test.js +++ b/web-client/src/presenter/computeds/extractPendingMessagesFromCaseDetail.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { extractedPendingMessagesFromCaseDetail as extractPendingMessagesFromCaseDetailComputed } from './extractPendingMessagesFromCaseDetail'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; @@ -23,7 +23,7 @@ const getBaseState = user => { }; const petitionsClerkUser = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; diff --git a/web-client/src/presenter/computeds/fileDocumentHelper.test.js b/web-client/src/presenter/computeds/fileDocumentHelper.test.js index cd2e9a53dad..9d20e8d9f1a 100644 --- a/web-client/src/presenter/computeds/fileDocumentHelper.test.js +++ b/web-client/src/presenter/computeds/fileDocumentHelper.test.js @@ -1,6 +1,6 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; import { MOCK_USERS } from '../../../../shared/src/test/mockUsers'; +import { PARTY_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { fileDocumentHelper as fileDocumentHelperComputed } from './fileDocumentHelper'; import { runCompute } from 'cerebral/test'; @@ -149,7 +149,7 @@ describe('fileDocumentHelper', () => { }); it('shows secondary party for petitionerSpouse or petitionerDeceasedSpouse', () => { - state.caseDetail.partyType = ContactFactory.PARTY_TYPES.petitionerSpouse; + state.caseDetail.partyType = PARTY_TYPES.petitionerSpouse; const result = runCompute(fileDocumentHelper, { state }); expect(result.showSecondaryParty).toBeTruthy(); }); diff --git a/web-client/src/presenter/computeds/filterWorkItems.test.js b/web-client/src/presenter/computeds/filterWorkItems.test.js index 507354a0bb1..24313bfe608 100644 --- a/web-client/src/presenter/computeds/filterWorkItems.test.js +++ b/web-client/src/presenter/computeds/filterWorkItems.test.js @@ -1,6 +1,11 @@ -import * as CONSTANTS from '../../../../shared/src/business/entities/WorkQueue'; -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + DOCKET_SECTION, + IRS_SYSTEM_SECTION, + PETITIONS_SECTION, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; + import { applicationContext } from '../../applicationContext'; import { filterWorkItems } from './formattedWorkQueue'; @@ -72,31 +77,31 @@ const SECTION_DOCUMENT_QC_OUTBOX = { }; const petitionsClerk1 = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: 'p1', }; const petitionsClerk2 = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: 'p2', }; const docketClerk1 = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, section: 'docket', userId: 'd1', }; const docketClerk2 = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, section: 'docket', userId: 'd2', }; const adc = { - role: User.ROLES.adc, + role: ROLES.adc, section: 'adc', userId: 'd3', }; @@ -106,7 +111,7 @@ const generateWorkItem = (data, document) => { assigneeId: null, assigneeName: null, caseId: '123', - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, createdAt: '2018-12-27T18:05:54.166Z', docketNumber: '100-01', document: { @@ -224,7 +229,7 @@ describe('filterWorkItems', () => { beforeAll(() => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '7f87f5d1-dfce-4515-a1e4-5231ceac61bb', }); @@ -233,15 +238,15 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-01', isQC: false, - section: CONSTANTS.PETITIONS_SECTION, + section: PETITIONS_SECTION, }); workItemPetitionsMyMessagesSent = generateWorkItem({ assigneeId: petitionsClerk2.userId, docketNumber: '100-02', isQC: false, - section: CONSTANTS.PETITIONS_SECTION, - sentBySection: CONSTANTS.PETITIONS_SECTION, + section: PETITIONS_SECTION, + sentBySection: PETITIONS_SECTION, sentByUserId: petitionsClerk1.userId, }); @@ -249,13 +254,13 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-03', isQC: false, - section: CONSTANTS.PETITIONS_SECTION, + section: PETITIONS_SECTION, }); workItemPetitionsSectionMessagesSent = generateWorkItem({ docketNumber: '100-04', isQC: false, - sentBySection: CONSTANTS.PETITIONS_SECTION, + sentBySection: PETITIONS_SECTION, sentByUserId: petitionsClerk2.userId, }); @@ -263,17 +268,17 @@ describe('filterWorkItems', () => { assigneeId: petitionsClerk1.userId, docketNumber: '100-05', isQC: true, - section: CONSTANTS.PETITIONS_SECTION, + section: PETITIONS_SECTION, }); workItemPetitionsMyDocumentQCServed = generateWorkItem({ assigneeId: petitionsClerk1.userId, - caseStatus: Case.STATUS_TYPES.calendared, + caseStatus: CASE_STATUS_TYPES.calendared, completedAt: '2019-07-18T18:05:54.166Z', completedByUserId: petitionsClerk1.userId, docketNumber: '100-07', isQC: true, - section: CONSTANTS.IRS_SYSTEM_SECTION, + section: IRS_SYSTEM_SECTION, sentByUserId: petitionsClerk1.userId, }); @@ -281,17 +286,17 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-08', isQC: true, - section: CONSTANTS.PETITIONS_SECTION, + section: PETITIONS_SECTION, }); workItemPetitionsSectionDocumentQCServed = generateWorkItem({ assigneeId: petitionsClerk2.userId, - caseStatus: Case.STATUS_TYPES.calendared, + caseStatus: CASE_STATUS_TYPES.calendared, completedAt: '2019-07-18T18:05:54.166Z', completedByUserId: petitionsClerk2.userId, docketNumber: '100-10', isQC: true, - section: CONSTANTS.IRS_SYSTEM_SECTION, + section: IRS_SYSTEM_SECTION, sentByUserId: petitionsClerk2.userId, }); @@ -300,14 +305,14 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-11', isQC: false, - section: CONSTANTS.DOCKET_SECTION, + section: DOCKET_SECTION, }); workItemDocketMyMessagesSent = generateWorkItem({ assigneeId: docketClerk2.userId, docketNumber: '100-12', isQC: false, - sentBySection: CONSTANTS.DOCKET_SECTION, + sentBySection: DOCKET_SECTION, sentByUserId: docketClerk1.userId, }); @@ -315,13 +320,13 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-13', isQC: false, - section: CONSTANTS.DOCKET_SECTION, + section: DOCKET_SECTION, }); workItemDocketSectionMessagesSent = generateWorkItem({ docketNumber: '100-14', isQC: false, - sentBySection: CONSTANTS.DOCKET_SECTION, + sentBySection: DOCKET_SECTION, sentByUserId: docketClerk2.userId, }); @@ -330,14 +335,14 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-15', isQC: true, - section: CONSTANTS.DOCKET_SECTION, + section: DOCKET_SECTION, }); workItemDocketSectionDocumentQCInbox = generateWorkItem({ completedAt: null, docketNumber: '100-17', isQC: true, - section: CONSTANTS.DOCKET_SECTION, + section: DOCKET_SECTION, }); workItemDocketMyDocumentQCInProgress = generateWorkItem( @@ -346,7 +351,7 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-18', isQC: true, - section: CONSTANTS.DOCKET_SECTION, + section: DOCKET_SECTION, }, { isFileAttached: false, @@ -359,7 +364,7 @@ describe('filterWorkItems', () => { completedAt: null, docketNumber: '100-19', isQC: true, - section: CONSTANTS.DOCKET_SECTION, + section: DOCKET_SECTION, }, { isFileAttached: false, @@ -498,7 +503,7 @@ describe('filterWorkItems', () => { it('Returns sent messages for a Petitions Clerk in My Document QC Outbox', () => { const filtered = workQueueOutbox.filter( filterWorkItems({ - USER_ROLES: User.ROLES, + USER_ROLES: ROLES, applicationContext, ...MY_DOCUMENT_QC_OUTBOX, user: petitionsClerk1, @@ -544,7 +549,7 @@ describe('filterWorkItems', () => { const user = petitionsClerk1; const filtered = workQueueOutbox.filter( filterWorkItems({ - USER_ROLES: User.ROLES, + USER_ROLES: ROLES, applicationContext, ...SECTION_DOCUMENT_QC_OUTBOX, user, diff --git a/web-client/src/presenter/computeds/formattedCaseDetail.js b/web-client/src/presenter/computeds/formattedCaseDetail.js index 692fc014719..9bce183d2ed 100644 --- a/web-client/src/presenter/computeds/formattedCaseDetail.js +++ b/web-client/src/presenter/computeds/formattedCaseDetail.js @@ -1,10 +1,17 @@ /* eslint-disable complexity */ import { state } from 'cerebral'; -export const formattedCases = (get, applicationContext) => { +export const formattedOpenCases = (get, applicationContext) => { const { formatCase } = applicationContext.getUtilities(); - const cases = get(state.cases); + const cases = get(state.openCases); + return cases.map(myCase => formatCase(applicationContext, myCase)); +}; + +export const formattedClosedCases = (get, applicationContext) => { + const { formatCase } = applicationContext.getUtilities(); + + const cases = get(state.closedCases); return cases.map(myCase => formatCase(applicationContext, myCase)); }; @@ -86,12 +93,10 @@ export const formattedCaseDetail = (get, applicationContext) => { filingsAndProceedingsWithAdditionalInfo += ` ${document.additionalInfo2}`; } - const isPaperAndNotServed = result.isPaper && result.status === 'New'; - const showDocumentEditLink = document && permissions.UPDATE_CASE && - ((!isPaperAndNotServed && !document.isInProgress) || + (!document.isInProgress || ((permissions.DOCKET_ENTRY || permissions.CREATE_ORDER_DOCKET_ENTRY) && document.isInProgress)); @@ -114,8 +119,6 @@ export const formattedCaseDetail = (get, applicationContext) => { permissions.DOCKET_ENTRY ) { editLink = '/edit'; - } else if (document.isPetition && !document.servedAt) { - editLink = '/review'; } } @@ -145,6 +148,8 @@ export const formattedCaseDetail = (get, applicationContext) => { isPaper, isPending: document && document.pending, isServed: document && !!document.servedAt, + numberOfPages: + (document && (record.numberOfPages || document.numberOfPages)) || 0, servedAtFormatted: document && document.servedAtFormatted, servedPartiesCode: record.servedPartiesCode || (document && document.servedPartiesCode), @@ -153,7 +158,6 @@ export const formattedCaseDetail = (get, applicationContext) => { (!userHasAccessToCase || !userHasAccessToDocument || !document || - isPaperAndNotServed || (document && (document.isNotServedCourtIssuedDocument || document.isInProgress) && @@ -195,7 +199,7 @@ export const formattedCaseDetail = (get, applicationContext) => { ...draftDocument, descriptionDisplay: draftDocument.documentTitle, editLink: '', - showDocumentEditLink: draftDocument && permissions.UPDATE_CASE, + showDocumentEditLink: permissions.UPDATE_CASE, }; }, ); diff --git a/web-client/src/presenter/computeds/formattedCaseDetail.test.js b/web-client/src/presenter/computeds/formattedCaseDetail.test.js index 7c8d33bf43a..abb6965c9e1 100644 --- a/web-client/src/presenter/computeds/formattedCaseDetail.test.js +++ b/web-client/src/presenter/computeds/formattedCaseDetail.test.js @@ -1,5 +1,7 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { formattedCaseDetail as formattedCaseDetailComputed } from './formattedCaseDetail'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; @@ -26,15 +28,15 @@ const getBaseState = user => { }; const petitionsClerkUser = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: '123', }; const docketClerkUser = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, userId: '234', }; const judgeUser = { - role: User.ROLES.judge, + role: ROLES.judge, userId: '345', }; @@ -55,6 +57,7 @@ describe('formattedCaseDetail', () => { it('maps docket record dates', () => { const caseDetail = { caseCaption: 'Brett Osborne, Petitioner', + correspondence: [], docketRecord: [ { description: 'Petition', @@ -78,6 +81,7 @@ describe('formattedCaseDetail', () => { it('maps docket record documents', () => { const caseDetail = { caseCaption: 'Brett Osborne, Petitioner', + correspondence: [], docketRecord: [ { description: 'Petition', @@ -119,6 +123,7 @@ describe('formattedCaseDetail', () => { contactSecondary: { name: 'Bill', }, + correspondence: [], docketRecord: [ { description: 'Amended Petition', @@ -328,6 +333,7 @@ describe('formattedCaseDetail', () => { contactPrimary: { name: 'Bob', }, + correspondence: [], docketRecord: [ { description: 'Order for Amended Petition', @@ -406,6 +412,7 @@ describe('formattedCaseDetail', () => { contactPrimary: { name: 'Bob', }, + correspondence: [], docketRecord: [ { description: 'Petition', @@ -468,7 +475,7 @@ describe('formattedCaseDetail', () => { workItems: [{ isQC: true }], }, ], - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }; const result = runCompute(formattedCaseDetail, { state: { @@ -480,7 +487,7 @@ describe('formattedCaseDetail', () => { expect(result.formattedDocketEntries).toMatchObject([ { - editLink: '/review', + editLink: '', showDocumentEditLink: true, }, { @@ -504,6 +511,7 @@ describe('formattedCaseDetail', () => { contactPrimary: { name: 'Bob', }, + correspondence: [], docketRecord: [ { description: 'Petition', @@ -537,7 +545,7 @@ describe('formattedCaseDetail', () => { }, ], isPaper: true, - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }; const result = runCompute(formattedCaseDetail, { state: { @@ -550,13 +558,13 @@ describe('formattedCaseDetail', () => { expect(result.formattedDocketEntries).toMatchObject([ { description: 'Petition', - showDocumentDescriptionWithoutLink: true, - showDocumentEditLink: false, + showDocumentDescriptionWithoutLink: false, + showDocumentEditLink: true, }, { description: 'Request for Place of Trial at Boise, Idaho', - showDocumentDescriptionWithoutLink: true, - showDocumentEditLink: false, + showDocumentDescriptionWithoutLink: false, + showDocumentEditLink: true, }, ]); }); @@ -567,6 +575,7 @@ describe('formattedCaseDetail', () => { sortedCaseDetail = { caseCaption: 'Brett Osborne, Petitioner', caseId: 'abdc-1234-5678-xyz', + correspondence: [], docketRecord: [ { description: 'Petition', @@ -759,6 +768,7 @@ describe('formattedCaseDetail', () => { describe('case name mapping', () => { it('should not error if caseCaption does not exist', () => { const caseDetail = { + correspondence: [], petitioners: [{ name: 'bob' }], }; const result = runCompute(formattedCaseDetail, { @@ -774,6 +784,7 @@ describe('formattedCaseDetail', () => { it("should remove ', Petitioner' from caseCaption", () => { const caseDetail = { caseCaption: 'Sisqo, Petitioner', + correspondence: [], petitioners: [{ name: 'bob' }], }; const result = runCompute(formattedCaseDetail, { @@ -789,6 +800,7 @@ describe('formattedCaseDetail', () => { it("should remove ', Petitioners' from caseCaption", () => { const caseDetail = { caseCaption: 'Sisqo and friends, Petitioners ', + correspondence: [], petitioners: [{ name: 'bob' }], }; const result = runCompute(formattedCaseDetail, { @@ -804,6 +816,7 @@ describe('formattedCaseDetail', () => { it("should remove ', Petitioner(s)' from caseCaption", () => { const caseDetail = { caseCaption: "Sisqo's entourage,, Petitioner(s) ", + correspondence: [], petitioners: [{ name: 'bob' }], }; const result = runCompute(formattedCaseDetail, { @@ -821,6 +834,7 @@ describe('formattedCaseDetail', () => { it('should add barNumber into formatted name if available', () => { const caseDetail = { caseCaption: 'Sisqo, Petitioner', + correspondence: [], petitioners: [{ name: 'bob' }], privatePractitioners: [{ barNumber: '9999', name: 'Jackie Chan' }], }; @@ -838,6 +852,7 @@ describe('formattedCaseDetail', () => { it('should not add barNumber into formatted name if not available', () => { const caseDetail = { caseCaption: 'Sisqo, Petitioner', + correspondence: [], petitioners: [{ name: 'bob' }], privatePractitioners: [{ name: 'Jackie Chan' }], }; @@ -858,8 +873,9 @@ describe('formattedCaseDetail', () => { it('should format trial information if a trial session id exists', () => { const caseDetail = { associatedJudge: 'Judge Judy', + correspondence: [], petitioners: [{ name: 'bob' }], - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: '2018-12-11T05:00:00Z', trialLocation: 'England is my City', trialSessionId: '123', @@ -880,8 +896,9 @@ describe('formattedCaseDetail', () => { it('should not add time if no time stamp exists', () => { const caseDetail = { associatedJudge: 'Judge Judy', + correspondence: [], petitioners: [{ name: 'bob' }], - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: '2018-12-11T05:00:00Z', trialLocation: 'England is my City', trialSessionId: '123', @@ -902,6 +919,7 @@ describe('formattedCaseDetail', () => { describe('formats case deadlines', () => { it('formats deadline dates, sorts them by date, and sets overdue to true if date is before today', () => { const caseDetail = { + correspondence: [], petitioners: [{ name: 'bob' }], }; const caseDeadlines = [ @@ -945,6 +963,7 @@ describe('formattedCaseDetail', () => { it('formats deadline dates and does not set overdue to true if the deadlineDate is today', () => { const caseDetail = { + correspondence: [], petitioners: [{ name: 'bob' }], }; const caseDeadlines = [ @@ -968,6 +987,7 @@ describe('formattedCaseDetail', () => { it('does not format empty caseDeadlines array', () => { const caseDetail = { caseDeadlines: [], + correspondence: [], petitioners: [{ name: 'bob' }], }; const result = runCompute(formattedCaseDetail, { @@ -987,6 +1007,7 @@ describe('formattedCaseDetail', () => { beforeAll(() => { caseDetail = { caseCaption: 'Brett Osborne, Petitioner', + correspondence: [], docketRecord: [ { description: 'Petition', @@ -1115,15 +1136,17 @@ describe('formattedCaseDetail', () => { consolidatedCases: [ { associatedJudge: 'Guy Fieri', + correspondence: [], petitioners: [{ name: 'Bobby Flay' }], - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: '2018-12-11T05:00:00Z', trialLocation: 'Flavortown', trialSessionId: '123', }, ], + correspondence: [], petitioners: [{ name: 'bob' }], - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: '2018-12-11T05:00:00Z', trialLocation: 'England is my City', trialSessionId: '123', @@ -1144,8 +1167,9 @@ describe('formattedCaseDetail', () => { it('should default consolidatedCases to an empty array if they do not exist', () => { const caseDetail = { associatedJudge: 'Judge Judy', + correspondence: [], petitioners: [{ name: 'bob' }], - status: Case.STATUS_TYPES.calendared, + status: CASE_STATUS_TYPES.calendared, trialDate: '2018-12-11T05:00:00Z', trialLocation: 'England is my City', trialSessionId: '123', @@ -1173,6 +1197,7 @@ describe('formattedCaseDetail', () => { contactPrimary: { name: 'Bob', }, + correspondence: [], docketRecord: [ { description: 'Motion to Dismiss for Lack of Jurisdiction', @@ -1377,4 +1402,143 @@ describe('formattedCaseDetail', () => { ).toEqual(true); }); }); + + describe('showEditDocketRecordEntry', () => { + let caseDetail; + + beforeAll(() => { + caseDetail = { + caseCaption: 'Brett Osborne, Petitioner', + contactPrimary: { + name: 'Bob', + }, + correspondence: [], + docketRecord: [ + { + description: 'Motion to Dismiss for Lack of Jurisdiction', + documentId: '69094dbb-72bf-481e-a592-8d50dad7ffa8', + filingDate: '2019-06-19T17:29:13.120Z', + numberOfPages: 24, + }, + { + description: 'Filing Fee Paid', + filingDate: '2019-06-19T17:29:13.120Z', + }, + { + description: 'System Generated', + documentId: '70094dbb-72bf-481e-a592-8d50dad7ffa9', + filingDate: '2019-06-19T17:29:13.120Z', + numberOfPages: 2, + }, + { + description: 'Court Issued - Not Served', + documentId: '80094dbb-72bf-481e-a592-8d50dad7ffa0', + filingDate: '2019-06-19T17:29:13.120Z', + numberOfPages: 7, + }, + { + description: 'Court Issued - Served', + documentId: '90094dbb-72bf-481e-a592-8d50dad7ffa1', + filingDate: '2019-06-19T17:29:13.120Z', + }, + ], + documents: [ + { + attachments: false, + certificateOfService: false, + createdAt: '2019-06-19T17:29:13.120Z', + documentId: '69094dbb-72bf-481e-a592-8d50dad7ffa8', + documentTitle: 'Motion to Dismiss for Lack of Jurisdiction', + documentType: 'Motion to Dismiss for Lack of Jurisdiction', + eventCode: 'M073', + workItems: [{ isQC: true }], + }, + { + attachments: false, + certificateOfService: false, + createdAt: '2019-06-19T17:29:13.120Z', + documentId: '70094dbb-72bf-481e-a592-8d50dad7ffa9', + documentTitle: 'System Generated', + documentType: 'Notice of Trial', + eventCode: 'NDT', + workItems: [{ isQC: true }], + }, + { + attachments: false, + certificateOfService: false, + createdAt: '2019-06-19T17:29:13.120Z', + documentId: '80094dbb-72bf-481e-a592-8d50dad7ffa0', + documentTitle: 'Court Issued - Not Served', + documentType: 'O - Order', + eventCode: 'O', + isCourtIssuedDocument: true, + workItems: [ + { completedAt: '2019-06-19T17:29:13.120Z', isQC: false }, + ], + }, + { + attachments: false, + certificateOfService: false, + createdAt: '2019-06-19T17:29:13.120Z', + documentId: '90094dbb-72bf-481e-a592-8d50dad7ffa1', + documentTitle: 'Court Issued - Served', + documentType: 'O - Order', + eventCode: 'O', + isCourtIssuedDocument: true, + numberOfPages: 9, + servedAt: '2019-06-19T17:29:13.120Z', + status: 'served', + workItems: [ + { completedAt: '2019-06-19T17:29:13.120Z', isQC: false }, + ], + }, + ], + }; + }); + + it('should show number of pages from the docket record with a mapped document', () => { + const result = runCompute(formattedCaseDetail, { + state: { + ...getBaseState(petitionsClerkUser), + caseDetail, + permissions: { + EDIT_DOCKET_ENTRY: true, + }, + validationErrors: {}, + }, + }); + + expect(result.formattedDocketEntries[0].numberOfPages).toEqual(24); + }); + + it('should show number of pages from the document', () => { + const result = runCompute(formattedCaseDetail, { + state: { + ...getBaseState(petitionsClerkUser), + caseDetail, + permissions: { + EDIT_DOCKET_ENTRY: true, + }, + validationErrors: {}, + }, + }); + + expect(result.formattedDocketEntries[4].numberOfPages).toEqual(9); + }); + + it('should show zero (0) number of pages with no document', () => { + const result = runCompute(formattedCaseDetail, { + state: { + ...getBaseState(petitionsClerkUser), + caseDetail, + permissions: { + EDIT_DOCKET_ENTRY: true, + }, + validationErrors: {}, + }, + }); + + expect(result.formattedDocketEntries[1].numberOfPages).toEqual(0); + }); + }); }); diff --git a/web-client/src/presenter/computeds/formattedDashboardTrialSessions.test.js b/web-client/src/presenter/computeds/formattedDashboardTrialSessions.test.js index 0c3e5300646..9b210fe1fc0 100644 --- a/web-client/src/presenter/computeds/formattedDashboardTrialSessions.test.js +++ b/web-client/src/presenter/computeds/formattedDashboardTrialSessions.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { formatNow } from '../../../../shared/src/business/utilities/DateHandler'; import { @@ -223,7 +223,7 @@ describe('formattedDashboardTrialSessions', () => { it('returns results for an associated chambers judge if the user role is chambers', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.chambers, + role: ROLES.chambers, userId: '6', }); const result = runCompute(formattedDashboardTrialSessions, { diff --git a/web-client/src/presenter/computeds/formattedMessageDetail.js b/web-client/src/presenter/computeds/formattedMessageDetail.js new file mode 100644 index 00000000000..b9dc91f6890 --- /dev/null +++ b/web-client/src/presenter/computeds/formattedMessageDetail.js @@ -0,0 +1,16 @@ +import { formatDateIfToday } from './formattedWorkQueue'; +import { state } from 'cerebral'; + +export const formattedMessageDetail = (get, applicationContext) => { + const messageDetail = get(state.messageDetail); + + const result = { + ...messageDetail, + createdAtFormatted: formatDateIfToday( + messageDetail.createdAt, + applicationContext, + ), + }; + + return result; +}; diff --git a/web-client/src/presenter/computeds/formattedMessageDetail.test.js b/web-client/src/presenter/computeds/formattedMessageDetail.test.js new file mode 100644 index 00000000000..b7116b30f27 --- /dev/null +++ b/web-client/src/presenter/computeds/formattedMessageDetail.test.js @@ -0,0 +1,29 @@ +import { applicationContext } from '../../applicationContext'; +import { formattedMessageDetail as formattedMessageDetailComputed } from './formattedMessageDetail'; +import { runCompute } from 'cerebral/test'; +import { withAppContextDecorator } from '../../withAppContext'; + +const formattedMessageDetail = withAppContextDecorator( + formattedMessageDetailComputed, + { + ...applicationContext, + }, +); + +describe('formattedMessageDetail', () => { + it('formats the message detail with createdAtFormatted', () => { + const result = runCompute(formattedMessageDetail, { + state: { + messageDetail: { + caseId: '78fb798f-66c3-42fa-bb5a-c14fac735b61', + createdAt: '2019-03-01T21:40:46.415Z', + messageId: '60e129bf-b8ec-4e0c-93c7-9633ab69f5df', + }, + }, + }); + + expect(result).toMatchObject({ + createdAtFormatted: '03/01/19', + }); + }); +}); diff --git a/web-client/src/presenter/computeds/formattedMessages.js b/web-client/src/presenter/computeds/formattedMessages.js new file mode 100644 index 00000000000..fa36b38c32d --- /dev/null +++ b/web-client/src/presenter/computeds/formattedMessages.js @@ -0,0 +1,17 @@ +import { state } from 'cerebral'; + +import { formatDateIfToday } from './formattedWorkQueue'; + +export const formattedMessages = (get, applicationContext) => { + const messages = get(state.messages) || []; + + const result = messages.map(message => ({ + ...message, + createdAtFormatted: formatDateIfToday( + message.createdAt, + applicationContext, + ), + })); + + return result; +}; diff --git a/web-client/src/presenter/computeds/formattedMessages.test.js b/web-client/src/presenter/computeds/formattedMessages.test.js new file mode 100644 index 00000000000..0e42780fff1 --- /dev/null +++ b/web-client/src/presenter/computeds/formattedMessages.test.js @@ -0,0 +1,34 @@ +import { formattedMessages as formattedMessagesComputed } from './formattedMessages'; +import { runCompute } from 'cerebral/test'; +import { withAppContextDecorator } from '../../withAppContext'; + +const formattedMessages = withAppContextDecorator(formattedMessagesComputed); + +describe('formattedMessages', () => { + it('returns a createdAtFormatted', () => { + const result = runCompute(formattedMessages, { + state: { + messages: [ + { + caseId: 'c6b81f4d-1e47-423a-8caf-6d2fdc3d3859', + caseStatus: 'Ready for trial', + createdAt: '2019-01-01T17:29:13.122Z', + docketNumber: '123-45', + docketNumberSuffix: '', + from: 'Test Sender', + fromSection: 'docket', + fromUserId: '11181f4d-1e47-423a-8caf-6d2fdc3d3859', + message: 'This is a test message', + messageId: '22281f4d-1e47-423a-8caf-6d2fdc3d3859', + subject: 'Test subject...', + to: 'Test Recipient', + toSection: 'petitions', + toUserId: '33331f4d-1e47-423a-8caf-6d2fdc3d3859', + }, + ], + }, + }); + + expect(result[0].createdAtFormatted).toEqual('01/01/19'); + }); +}); diff --git a/web-client/src/presenter/computeds/formattedTrialSessions.test.js b/web-client/src/presenter/computeds/formattedTrialSessions.test.js index a7668e75aa9..8442f4d09a5 100644 --- a/web-client/src/presenter/computeds/formattedTrialSessions.test.js +++ b/web-client/src/presenter/computeds/formattedTrialSessions.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { filterFormattedSessionsByStatus, @@ -28,17 +28,17 @@ let nextYear; let currentUser = {}; const testJudgeUser = { - role: User.ROLES.judge, + role: ROLES.judge, userId: '1', }; const testTrialClerkUser = { - role: User.ROLES.trialClerk, + role: ROLES.trialClerk, userId: '10', }; const baseState = { - constants: { USER_ROLES: User.ROLES }, + constants: { USER_ROLES: ROLES }, judgeUser: testJudgeUser, }; @@ -456,7 +456,7 @@ describe('formattedTrialSessions', () => { ...baseState, judgeUser: undefined, trialSessions: TRIAL_SESSIONS_LIST, - user: { role: User.ROLES.petitionsClerk, userId: '1' }, + user: { role: ROLES.petitionsClerk, userId: '1' }, }, }); expect(result.formattedSessions).toMatchObject([ @@ -612,7 +612,7 @@ describe('formattedTrialSessions', () => { trialLocation: 'Jacksonville, FL', }, ], - user: { role: User.ROLES.petitionsClerk, userId: '1' }, + user: { role: ROLES.petitionsClerk, userId: '1' }, }, }); expect(result.formattedSessions).toMatchObject([ diff --git a/web-client/src/presenter/computeds/formattedWorkQueue.js b/web-client/src/presenter/computeds/formattedWorkQueue.js index 2d9c2e0d460..94d02b3f001 100644 --- a/web-client/src/presenter/computeds/formattedWorkQueue.js +++ b/web-client/src/presenter/computeds/formattedWorkQueue.js @@ -1,7 +1,7 @@ import { DOCKET_SECTION, PETITIONS_SECTION, -} from '../../../../shared/src/business/entities/WorkQueue'; +} from '../../../../shared/src/business/entities/EntityConstants'; import { capitalize, cloneDeep, orderBy } from 'lodash'; import { filterQcItemsByAssociatedJudge } from '../utilities/filterQcItemsByAssociatedJudge'; import { state } from 'cerebral'; @@ -14,7 +14,7 @@ const isDateToday = (date, applicationContext) => { return now === then; }; -const formatDateIfToday = (date, applicationContext) => { +export const formatDateIfToday = (date, applicationContext) => { const now = applicationContext.getUtilities().formatNow('MMDDYY'); const then = applicationContext .getUtilities() @@ -140,6 +140,28 @@ export const formatWorkItem = ({ return result; }; +const getDocketEntryEditLink = ({ + formattedDocument, + isInProgress, + qcWorkItemsUntouched, + result, +}) => { + let editLink; + if (formattedDocument.isCourtIssuedDocument && !formattedDocument.servedAt) { + editLink = '/edit-court-issued'; + } else if (isInProgress) { + editLink = '/complete'; + } else if ( + !result.isCourtIssuedDocument && + !result.isOrder && + !formattedDocument.isPetition && + qcWorkItemsUntouched + ) { + editLink = '/edit'; + } + return editLink; +}; + export const getWorkItemDocumentLink = ({ applicationContext, permissions, @@ -168,40 +190,28 @@ export const getWorkItemDocumentLink = ({ (!formattedDocument.isInProgress || (permissions.DOCKET_ENTRY && formattedDocument.isInProgress)); - let editLink; //defaults to doc detail - if ( - showDocumentEditLink && - permissions.DOCKET_ENTRY && - formattedDocument && - !workQueueIsInternal - ) { - if ( - formattedDocument.isCourtIssuedDocument && - !formattedDocument.servedAt - ) { - editLink = '/edit-court-issued'; - } else if (isInProgress) { - editLink = '/complete'; - } else if ( - !result.isCourtIssuedDocument && - !result.isOrder && - !formattedDocument.isPetition && - qcWorkItemsUntouched - ) { - editLink = '/edit'; + const documentDetailLink = `/case-detail/${workItem.docketNumber}/documents/${workItem.document.documentId}`; + let editLink = documentDetailLink; + if (showDocumentEditLink && !workQueueIsInternal) { + if (permissions.DOCKET_ENTRY) { + const editLinkExtension = getDocketEntryEditLink({ + formattedDocument, + isInProgress, + qcWorkItemsUntouched, + result, + }); + if (editLinkExtension) { + editLink += editLinkExtension; + } + } else if (formattedDocument.isPetition && !formattedDocument.servedAt) { + if (result.caseIsInProgress) { + editLink += '/review'; + } else { + editLink = `/case-detail/${workItem.docketNumber}/petition-qc`; + } } - } else if ( - showDocumentEditLink && - permissions.UPDATE_CASE && - formattedDocument && - !workQueueIsInternal && - formattedDocument.isPetition && - result.caseIsInProgress && - !formattedDocument.servedAt - ) { - editLink = '/review'; } - if (!editLink) { + if (editLink === documentDetailLink) { const messageId = result.messages[0] && result.messages[0].messageId; const workItemIdToMarkAsRead = !result.isRead ? result.workItemId : null; @@ -213,7 +223,7 @@ export const getWorkItemDocumentLink = ({ if (messageId && (workQueueIsInternal || permissions.DOCKET_ENTRY)) { editLink = `/messages/${messageId}${markReadPath}`; - } else { + } else if (markReadPath) { editLink = `${markReadPath}`; } } diff --git a/web-client/src/presenter/computeds/formattedWorkQueue.test.js b/web-client/src/presenter/computeds/formattedWorkQueue.test.js index 73246accbc9..94579b53c94 100644 --- a/web-client/src/presenter/computeds/formattedWorkQueue.test.js +++ b/web-client/src/presenter/computeds/formattedWorkQueue.test.js @@ -1,8 +1,12 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + CASE_STATUS_TYPES, + CHIEF_JUDGE, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { cloneDeep } from 'lodash'; import { + formatDateIfToday, formatWorkItem, formattedWorkQueue as formattedWorkQueueComputed, getWorkItemDocumentLink, @@ -29,13 +33,13 @@ const WORK_ITEM_ID_4 = '4bd51fb7-fc46-4d4d-a506-08d48afcf46d'; const JUDGE_USER_ID_1 = '89c956aa-65c6-4632-a6c8-7f0c6162d615'; const petitionsClerkUser = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, section: 'petitions', userId: 'abc', }; const docketClerkUser = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, section: 'docket', userId: 'abc', }; @@ -51,7 +55,7 @@ const FORMATTED_WORK_ITEM = { assigneeId: 'abc', assigneeName: 'Unassigned', caseId: 'e631d81f-a579-4de5-b8a8-b3f10ef619fd', - caseStatus: Case.STATUS_TYPES.generalDocket, + caseStatus: CASE_STATUS_TYPES.generalDocket, createdAtFormatted: '12/27/18', currentMessage: { createdAtFormatted: '12/27/18', @@ -111,7 +115,7 @@ describe('formatted work queue computed', () => { assigneeId: 'abc', assigneeName: null, caseId: 'e631d81f-a579-4de5-b8a8-b3f10ef619fd', - caseStatus: Case.STATUS_TYPES.generalDocket, + caseStatus: CASE_STATUS_TYPES.generalDocket, createdAt: '2018-12-27T18:05:54.166Z', docketNumber: '101-18', docketNumberSuffix: 'S', @@ -435,7 +439,7 @@ describe('formatted work queue computed', () => { it('filters items based on associatedJudge for a given judge or chambers user', () => { const judgeUser = { name: 'Test Judge', - role: User.ROLES.judge, + role: ROLES.judge, userId: JUDGE_USER_ID_1, }; @@ -460,7 +464,7 @@ describe('formatted work queue computed', () => { }, { ...qcWorkItem, - associatedJudge: Case.CHIEF_JUDGE, + associatedJudge: CHIEF_JUDGE, workItemId: WORK_ITEM_ID_4, }, ], @@ -479,7 +483,7 @@ describe('formatted work queue computed', () => { it('filters items based on associatedJudge for an adc user', () => { const adcUser = { name: 'Test ADC', - role: User.ROLES.adc, + role: ROLES.adc, userId: 'd4d25c47-bb50-4575-9c31-d00bb682a215', }; @@ -503,7 +507,7 @@ describe('formatted work queue computed', () => { }, { ...qcWorkItem, - associatedJudge: Case.CHIEF_JUDGE, + associatedJudge: CHIEF_JUDGE, workItemId: WORK_ITEM_ID_4, }, ], @@ -523,7 +527,7 @@ describe('formatted work queue computed', () => { it('filters items based on in progress cases for a petitionsclerk', () => { const petitionsClerkUser = { name: 'Test PetitionsClerk', - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, userId: 'd4d25c47-bb50-4575-9c31-d00bb682a215', }; @@ -547,9 +551,9 @@ describe('formatted work queue computed', () => { }, { ...qcWorkItem, - associatedJudge: Case.CHIEF_JUDGE, + associatedJudge: CHIEF_JUDGE, caseIsInProgress: true, - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, document: { ...qcWorkItem.document, status: 'processing', @@ -761,9 +765,11 @@ describe('formatted work queue computed', () => { message: 'Petition filed by Ori Petersen is ready for review.', messageId: '9ad0fceb-41be-4902-8294-9f505fb7a353', }; + const baseWorkItemEditLink = + '/case-detail/114-19/documents/6db35185-2445-4952-9449-5479a5cadab0'; - it('should return editLink as default document detail page if document is petition and user is petitionsclerk viewing a QC box (workQueueIsInternal=false)', () => { - const permissions = getBaseState(petitionsClerkUser); + it('should return editLink as petition qc page if document is petition, case is not in progress, and user is petitionsclerk viewing a QC box (workQueueIsInternal=false)', () => { + const { permissions } = getBaseState(petitionsClerkUser); const result = getWorkItemDocumentLink({ applicationContext, @@ -787,7 +793,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual(''); + expect(result).toEqual('/case-detail/114-19/petition-qc'); }); it('should return /edit-court-issued if document is court-issued and not served and user is docketclerk', () => { @@ -815,7 +821,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual('/edit-court-issued'); + expect(result).toEqual(`${baseWorkItemEditLink}/edit-court-issued`); }); it('should return editLink as default document detail page if document is court-issued and not served and user is petitionsclerk viewing a QC box (workQueueIsInternal=false)', () => { @@ -843,7 +849,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual(''); + expect(result).toEqual(baseWorkItemEditLink); }); it('should return /complete if document is in progress and user is docketclerk', () => { @@ -878,7 +884,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual('/complete'); + expect(result).toEqual(`${baseWorkItemEditLink}/complete`); }); it('should return default edit link if document is in progress and user is petitionsClerk', () => { @@ -913,7 +919,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual(''); + expect(result).toEqual(baseWorkItemEditLink); }); it("should return /edit if document is an external doc that has not been qc'd (isQC is true) and user is docketclerk", () => { @@ -946,7 +952,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual('/edit'); + expect(result).toEqual(`${baseWorkItemEditLink}/edit`); }); it("should return editLink with a direct link to the message if document is an external doc that has not been qc'd (isQC is true) and user is petitionsClerk and viewing a messages box (workQueueIsInternal=true)", () => { @@ -1047,7 +1053,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual('/edit'); + expect(result).toEqual(`${baseWorkItemEditLink}/edit`); }); it('should return editLink as /review if the box is my inProgress and user is petitionsClerk', () => { @@ -1076,7 +1082,7 @@ describe('formatted work queue computed', () => { workQueueIsInternal: false, }, }); - expect(result).toEqual('/review'); + expect(result).toEqual(`${baseWorkItemEditLink}/review`); }); }); @@ -1455,4 +1461,42 @@ describe('formatted work queue computed', () => { ); }); }); + + describe('formatDateIfToday', () => { + it('returns a time if the date is today', () => { + const currentTime = applicationContext + .getUtilities() + .createISODateString(); + + const result = formatDateIfToday(currentTime, applicationContext); + + expect(result).toContain(':'); + expect(result).toContain('ET'); + expect(result).not.toContain('/'); + }); + + it('returns "Yesterday" if the date is yesterday', () => { + const currentTime = applicationContext + .getUtilities() + .createISODateString(); + + const yesterday = applicationContext + .getUtilities() + .calculateISODate({ dateString: currentTime, howMuch: -1 }); + + const result = formatDateIfToday(yesterday, applicationContext); + + expect(result).toEqual('Yesterday'); + }); + + it('returns the formatted date if older than one day', () => { + const date = applicationContext + .getUtilities() + .formatDateString('2019-01-01T17:29:13.122Z'); + + const result = formatDateIfToday(date, applicationContext); + + expect(result).toContain('01/01/19'); + }); + }); }); diff --git a/web-client/src/presenter/computeds/headerHelper.js b/web-client/src/presenter/computeds/headerHelper.js index 35871fb1bab..a6bae5e8e49 100644 --- a/web-client/src/presenter/computeds/headerHelper.js +++ b/web-client/src/presenter/computeds/headerHelper.js @@ -18,6 +18,7 @@ export const headerHelper = (get, applicationContext) => { const isTrialSessions = currentPage.includes('TrialSession'); const isDashboard = currentPage.startsWith('Dashboard'); const isMessages = currentPage.startsWith('Messages'); + const isCaseMessages = currentPage.startsWith('CaseMessages'); const pageIsHome = isDashboard || @@ -34,6 +35,7 @@ export const headerHelper = (get, applicationContext) => { defaultQCBoxPath: isOtherUser(userRole) ? '/document-qc/section/inbox' : '/document-qc/my/inbox', + pageIsCaseMessages: isCaseMessages && workQueueIsInternal, pageIsDashboard: isDashboard && applicationContext.getUtilities().isExternalUser(userRole), pageIsDocumentQC: isMessages && !workQueueIsInternal, diff --git a/web-client/src/presenter/computeds/headerHelper.test.js b/web-client/src/presenter/computeds/headerHelper.test.js index 2dff2f95869..25f122b680d 100644 --- a/web-client/src/presenter/computeds/headerHelper.test.js +++ b/web-client/src/presenter/computeds/headerHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; import { headerHelper as headerHelperComputed } from './headerHelper'; @@ -20,31 +20,27 @@ const getBaseState = user => { }; }; -const internal = [ - User.ROLES.petitionsClerk, - User.ROLES.adc, - User.ROLES.docketClerk, -]; +const internal = [ROLES.petitionsClerk, ROLES.adc, ROLES.docketClerk]; const external = [ - User.ROLES.petitioner, - User.ROLES.privatePractitioner, - User.ROLES.irsPractitioner, + ROLES.petitioner, + ROLES.privatePractitioner, + ROLES.irsPractitioner, ]; describe('headerHelper', () => { it('should show search in header for users other than petitioner, privatePractitioners and irsPractitioners', () => { let result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.petitioner }), + state: getBaseState({ role: ROLES.petitioner }), }); expect(result.showSearchInHeader).toBeFalsy(); result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.petitionsClerk }), + state: getBaseState({ role: ROLES.petitionsClerk }), }); expect(result.showSearchInHeader).toBeTruthy(); result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.docketClerk }), + state: getBaseState({ role: ROLES.docketClerk }), }); expect(result.showSearchInHeader).toBeTruthy(); }); @@ -83,35 +79,35 @@ describe('headerHelper', () => { }); it('should not show cases for irsSuperuser', () => { const result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.irsSuperuser }), + state: getBaseState({ role: ROLES.irsSuperuser }), }); expect(result.showMyCases).toBeFalsy(); }); it('should show search nav item for irsSuperuser', () => { const result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.irsSuperuser }), + state: getBaseState({ role: ROLES.irsSuperuser }), }); expect(result.showSearchNavItem).toBeTruthy(); }); it('should NOT show search nav item for privatePractitioner', () => { const result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.privatePractitioner }), + state: getBaseState({ role: ROLES.privatePractitioner }), }); expect(result.showSearchNavItem).toBeFalsy(); }); it('should NOT show search in header for privatePractitioners, irsPractitioners, or irsSuperuser', () => { let result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.privatePractitioner }), + state: getBaseState({ role: ROLES.privatePractitioner }), }); expect(result.showSearchInHeader).toBeFalsy(); result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.irsPractitioner }), + state: getBaseState({ role: ROLES.irsPractitioner }), }); expect(result.showSearchInHeader).toBeFalsy(); result = runCompute(headerHelper, { - state: getBaseState({ role: User.ROLES.irsSuperuser }), + state: getBaseState({ role: ROLES.irsSuperuser }), }); expect(result.showSearchInHeader).toBeFalsy(); }); @@ -150,7 +146,7 @@ describe('headerHelper', () => { it('should know when the page is Messages', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.petitionsClerk }), + ...getBaseState({ role: ROLES.petitionsClerk }), currentPage: 'Messages', workQueueToDisplay: { workQueueIsInternal: true, @@ -159,10 +155,22 @@ describe('headerHelper', () => { }); expect(result.pageIsMessages).toBeTruthy(); }); + it('should know when the page is Case Messages', () => { + const result = runCompute(headerHelper, { + state: { + ...getBaseState({ role: ROLES.petitionsClerk }), + currentPage: 'CaseMessages', + workQueueToDisplay: { + workQueueIsInternal: true, + }, + }, + }); + expect(result.pageIsCaseMessages).toBeTruthy(); + }); it('should know when the page is My Cases', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.petitioner }), + ...getBaseState({ role: ROLES.petitioner }), currentPage: 'DashboardPetitioner', }, }); @@ -171,7 +179,7 @@ describe('headerHelper', () => { it('should know when the page is Dashboard', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.irsSuperuser }), + ...getBaseState({ role: ROLES.irsSuperuser }), currentPage: 'DashboardIrsSuperuser', }, }); @@ -180,7 +188,7 @@ describe('headerHelper', () => { it('should not set pageIsMessages or pageIsDocumentQC to true if currentPage is TrialSessions', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.petitionsClerk }), + ...getBaseState({ role: ROLES.petitionsClerk }), currentPage: 'TrialSessions', }, }); @@ -190,7 +198,7 @@ describe('headerHelper', () => { it('should know when the page is TrialSessions', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.petitionsClerk }), + ...getBaseState({ role: ROLES.petitionsClerk }), currentPage: 'TrialSessions', }, }); @@ -199,7 +207,7 @@ describe('headerHelper', () => { it('should know when the page is TrialSessionDetails', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.petitionsClerk }), + ...getBaseState({ role: ROLES.petitionsClerk }), currentPage: 'TrialSessionDetails', }, }); @@ -209,7 +217,7 @@ describe('headerHelper', () => { it('should show border under Reports tab when page is CaseDeadlines', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.petitionsClerk }), + ...getBaseState({ role: ROLES.petitionsClerk }), currentPage: 'CaseDeadlines', }, }); @@ -219,7 +227,7 @@ describe('headerHelper', () => { it('should show border under Reports tab when page is BlockedCasesReport', () => { const result = runCompute(headerHelper, { state: { - ...getBaseState({ role: User.ROLES.petitionsClerk }), + ...getBaseState({ role: ROLES.petitionsClerk }), currentPage: 'BlockedCasesReport', }, }); diff --git a/web-client/src/presenter/computeds/mapValueHelper.js b/web-client/src/presenter/computeds/mapValueHelper.js deleted file mode 100644 index 61f3b43200c..00000000000 --- a/web-client/src/presenter/computeds/mapValueHelper.js +++ /dev/null @@ -1,37 +0,0 @@ -import { - camelCase, - isArray, - isBoolean, - isNumber, - isPlainObject, - isString, - map, -} from 'lodash'; - -export const mapValueHelper = value => { - let object = {}; - - if (isString(value)) { - const key = camelCase(value); - object[key] = true; - } - - if (isBoolean(value) || isNumber(value)) { - const key = value.toString(); - object[key] = true; - } - - if (isArray(value)) { - for (let i = 0; i < value.length; i++) { - object[i.toString()] = mapValueHelper(value[i]); - } - } - - if (isPlainObject(value)) { - map(value, (thisValue, key) => { - object[key] = mapValueHelper(thisValue); - }); - } - - return object; -}; diff --git a/web-client/src/presenter/computeds/mapValueHelper.test.js b/web-client/src/presenter/computeds/mapValueHelper.test.js deleted file mode 100644 index b5f12ec6573..00000000000 --- a/web-client/src/presenter/computeds/mapValueHelper.test.js +++ /dev/null @@ -1,55 +0,0 @@ -import { mapValueHelper } from './mapValueHelper'; - -describe('mapValueHelper', () => { - describe('value is undefined', () => { - it('should return an empty object', () => { - expect(mapValueHelper()).toEqual({}); - }); - }); - - describe('value is string', () => { - it('should map the value into an object property with the value true', () => { - expect(mapValueHelper('something')).toEqual({ something: true }); - }); - - it('should map the value into an camel-cased object property with the value true', () => { - expect(mapValueHelper('some thing')).toEqual({ someThing: true }); - }); - }); - - describe('value is boolean', () => { - it('should map the value into an object property named true with the value true', () => { - expect(mapValueHelper(true)).toEqual({ true: true }); - }); - - it('should map the value into an object property named false with the value true', () => { - expect(mapValueHelper(false)).toEqual({ false: true }); - }); - }); - - describe('value is number', () => { - it('should map the value as a string into an object property with the value true', () => { - expect(mapValueHelper(8675309)).toEqual({ '8675309': true }); - }); - }); - - describe('value is array', () => { - it('should map each item as property of index and map the value of each item.', () => { - expect(mapValueHelper([true, false, 'something', 8675309])).toEqual({ - '0': { true: true }, - '1': { false: true }, - '2': { something: true }, - '3': { '8675309': true }, - }); - }); - }); - - describe('value is object', () => { - it('should map each item as property and map the value of each item.', () => { - expect(mapValueHelper({ firstName: 'Josh', lastName: 'Allen' })).toEqual({ - firstName: { josh: true }, - lastName: { allen: true }, - }); - }); - }); -}); diff --git a/web-client/src/presenter/computeds/messagesHelper.js b/web-client/src/presenter/computeds/messagesHelper.js new file mode 100644 index 00000000000..ea213e4a1cb --- /dev/null +++ b/web-client/src/presenter/computeds/messagesHelper.js @@ -0,0 +1,13 @@ +import { state } from 'cerebral'; + +export const messagesHelper = get => { + const messageBoxToDisplay = get(state.messageBoxToDisplay); + const showIndividualMessages = messageBoxToDisplay.queue === 'my'; + const showSectionMessages = messageBoxToDisplay.queue === 'section'; + + const messagesTitle = showIndividualMessages + ? 'My Messages' + : 'Section Messages'; + + return { messagesTitle, showIndividualMessages, showSectionMessages }; +}; diff --git a/web-client/src/presenter/computeds/messagesHelper.test.js b/web-client/src/presenter/computeds/messagesHelper.test.js new file mode 100644 index 00000000000..6973339df47 --- /dev/null +++ b/web-client/src/presenter/computeds/messagesHelper.test.js @@ -0,0 +1,50 @@ +import { messagesHelper } from './messagesHelper'; +import { runCompute } from 'cerebral/test'; + +describe('messagesHelper', () => { + it('should set showIndividualMessages true and showSectionMessages false if messageBoxToDisplay.queue is my', () => { + const result = runCompute(messagesHelper, { + state: { + messageBoxToDisplay: { + queue: 'my', + }, + }, + }); + expect(result.showIndividualMessages).toBeTruthy(); + expect(result.showSectionMessages).toBeFalsy(); + }); + + it('should set showIndividualMessages false and showSectionMessages true if messageBoxToDisplay.queue is section', () => { + const result = runCompute(messagesHelper, { + state: { + messageBoxToDisplay: { + queue: 'section', + }, + }, + }); + expect(result.showIndividualMessages).toBeFalsy(); + expect(result.showSectionMessages).toBeTruthy(); + }); + + it('should set messagesTitle to the current message box being displayed', () => { + let result = runCompute(messagesHelper, { + state: { + messageBoxToDisplay: { + queue: 'my', + }, + }, + }); + + expect(result.messagesTitle).toEqual('My Messages'); + + result = runCompute(messagesHelper, { + state: { + messageBoxToDisplay: { + queue: 'section', + }, + }, + }); + + expect(result.messagesTitle).toEqual('Section Messages'); + }); +}); diff --git a/web-client/src/presenter/computeds/public/publicCaseDetailHelper.js b/web-client/src/presenter/computeds/public/publicCaseDetailHelper.js index 4f57527c201..9e826f2ae2c 100644 --- a/web-client/src/presenter/computeds/public/publicCaseDetailHelper.js +++ b/web-client/src/presenter/computeds/public/publicCaseDetailHelper.js @@ -54,6 +54,8 @@ export const publicCaseDetailHelper = (get, applicationContext) => { hasDocument: !!document, index, isPaper: document && document.isPaper, + numberOfPages: + (document && (record.numberOfPages || document.numberOfPages)) || 0, servedAtFormatted: document && document.servedAtFormatted, servedPartiesCode: document && document.servedPartiesCode, showDocumentDescriptionWithoutLink: diff --git a/web-client/src/presenter/computeds/public/todaysOpinionsHelper.js b/web-client/src/presenter/computeds/public/todaysOpinionsHelper.js new file mode 100644 index 00000000000..abbb67d6c01 --- /dev/null +++ b/web-client/src/presenter/computeds/public/todaysOpinionsHelper.js @@ -0,0 +1,26 @@ +import { Document } from '../../../../../shared/src/business/entities/Document'; +import { state } from 'cerebral'; + +export const todaysOpinionsHelper = (get, applicationContext) => { + const todaysOpinions = get(state.todaysOpinions); + const baseUrl = get(state.baseUrl); + + const currentDate = applicationContext.getUtilities().createISODateString(); + const formattedCurrentDate = applicationContext + .getUtilities() + .formatDateString(currentDate, 'MMMM D, YYYY'); + + const formattedOpinions = todaysOpinions.map(opinion => ({ + ...opinion, + documentLink: `${baseUrl}/public-api/${opinion.caseId}/${opinion.documentId}/public-document-download-url`, + formattedDocumentType: Document.getFormattedType(opinion.documentType), + formattedFilingDate: applicationContext + .getUtilities() + .formatDateString(opinion.filingDate, 'MMDDYY'), + formattedJudgeName: applicationContext + .getUtilities() + .getJudgeLastName(opinion.judge), + })); + + return { formattedCurrentDate, formattedOpinions }; +}; diff --git a/web-client/src/presenter/computeds/public/todaysOpinionsHelper.test.js b/web-client/src/presenter/computeds/public/todaysOpinionsHelper.test.js new file mode 100644 index 00000000000..1eac031f4a6 --- /dev/null +++ b/web-client/src/presenter/computeds/public/todaysOpinionsHelper.test.js @@ -0,0 +1,56 @@ +import { applicationContextPublic } from '../../../applicationContextPublic'; +import { runCompute } from 'cerebral/test'; +import { todaysOpinionsHelper as todaysOpinionsHelperComputed } from './todaysOpinionsHelper'; +import { withAppContextDecorator } from '../../../withAppContext'; + +const todaysOpinionsHelper = withAppContextDecorator( + todaysOpinionsHelperComputed, + applicationContextPublic, +); + +let state; +describe('todaysOpinionsHelper', () => { + beforeEach(() => { + state = { + baseUrl: 'https://www.example.com', + todaysOpinions: [ + { + caseCaption: 'Sauceboss, Petitioner', + caseId: 'case-id-123', + documentId: 'document-id-123', + documentType: 'MOP - Memorandum Opinion', + filingDate: '2020-06-11T20:17:10.646Z', + judge: 'Guy Fieri', + }, + ], + }; + }); + + it('should return the formattedOpinions as an array', () => { + const result = runCompute(todaysOpinionsHelper, { state }); + expect(Array.isArray(result.formattedOpinions)).toBeTruthy(); + expect(result.formattedOpinions).toMatchObject([ + { + caseCaption: 'Sauceboss, Petitioner', + documentLink: + 'https://www.example.com/public-api/case-id-123/document-id-123/public-document-download-url', + formattedDocumentType: 'Memorandum Opinion', + formattedFilingDate: '06/11/20', + formattedJudgeName: 'Fieri', + }, + ]); + }); + + it('should return formattedCurrentDate', () => { + const result = runCompute(todaysOpinionsHelper, { state }); + + const currentDate = applicationContextPublic + .getUtilities() + .createISODateString(); + const formattedCurrentDate = applicationContextPublic + .getUtilities() + .formatDateString(currentDate, 'MMMM D, YYYY'); + + expect(result.formattedCurrentDate).toEqual(formattedCurrentDate); + }); +}); diff --git a/web-client/src/presenter/computeds/requestAccessHelper.test.js b/web-client/src/presenter/computeds/requestAccessHelper.test.js index d4cf386c718..01d7e97992d 100644 --- a/web-client/src/presenter/computeds/requestAccessHelper.test.js +++ b/web-client/src/presenter/computeds/requestAccessHelper.test.js @@ -1,5 +1,5 @@ import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { requestAccessHelper as requestAccessHelperComputed } from './requestAccessHelper'; import { runCompute } from 'cerebral/test'; @@ -17,7 +17,7 @@ const requestAccessHelper = withAppContextDecorator( ); applicationContext.getCurrentUser = () => ({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); describe('requestAccessHelper', () => { @@ -90,7 +90,7 @@ describe('requestAccessHelper', () => { it('returns correct number of document options for user role respondent', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }); const result = runCompute(requestAccessHelper, { state }); expect(result.documents.length).toEqual(2); diff --git a/web-client/src/presenter/computeds/reviewSavedPetitionHelper.js b/web-client/src/presenter/computeds/reviewSavedPetitionHelper.js index e1036b3f91c..e0ff99f9c13 100644 --- a/web-client/src/presenter/computeds/reviewSavedPetitionHelper.js +++ b/web-client/src/presenter/computeds/reviewSavedPetitionHelper.js @@ -1,3 +1,4 @@ +import { formatStatistic } from './statisticsHelper'; import { state } from 'cerebral'; export const reviewSavedPetitionHelper = (get, applicationContext) => { @@ -90,28 +91,9 @@ export const reviewSavedPetitionHelper = (get, applicationContext) => { const showStatistics = statistics && statistics.length > 0; - const formattedStatistics = (statistics || []).map(statistic => { - const formattedDate = - statistic.year || - applicationContext - .getUtilities() - .formatDateString(statistic.lastDateOfPeriod, 'MMDDYY'); - - const formattedDeficiencyAmount = applicationContext - .getUtilities() - .formatDollars(statistic.deficiencyAmount); - - const formattedTotalPenalties = applicationContext - .getUtilities() - .formatDollars(statistic.totalPenalties); - - return { - ...statistic, - formattedDate, - formattedDeficiencyAmount, - formattedTotalPenalties, - }; - }); + const formattedStatistics = (statistics || []).map(statistic => + formatStatistic({ applicationContext, statistic }), + ); return { applicationForWaiverOfFilingFeeFile, diff --git a/web-client/src/presenter/computeds/reviewSavedPetitionHelper.test.js b/web-client/src/presenter/computeds/reviewSavedPetitionHelper.test.js index 8e02618730d..a430fa5d03e 100644 --- a/web-client/src/presenter/computeds/reviewSavedPetitionHelper.test.js +++ b/web-client/src/presenter/computeds/reviewSavedPetitionHelper.test.js @@ -216,15 +216,15 @@ describe('reviewSavedPetitionHelper', () => { form: { statistics: [ { - deficiencyAmount: 123, - totalPenalties: 30000, + irsDeficiencyAmount: 123, + irsTotalPenalties: 30000, year: '2012', yearOrPeriod: 'Year', }, { - deficiencyAmount: 0, + irsDeficiencyAmount: 0, + irsTotalPenalties: 21, lastDateOfPeriod: '2019-03-01T21:40:46.415Z', - totalPenalties: 21, yearOrPeriod: 'Period', }, ], @@ -235,13 +235,13 @@ describe('reviewSavedPetitionHelper', () => { expect(result.formattedStatistics).toMatchObject([ { formattedDate: '2012', - formattedDeficiencyAmount: '$123.00', - formattedTotalPenalties: '$30,000.00', + formattedIrsDeficiencyAmount: '$123.00', + formattedIrsTotalPenalties: '$30,000.00', }, { formattedDate: '03/01/19', - formattedDeficiencyAmount: '$0.00', - formattedTotalPenalties: '$21.00', + formattedIrsDeficiencyAmount: '$0.00', + formattedIrsTotalPenalties: '$21.00', }, ]); }); diff --git a/web-client/src/presenter/computeds/scanBatchPreviewerHelper.test.js b/web-client/src/presenter/computeds/scanBatchPreviewerHelper.test.js index 77b59fdd3a0..b4d9e4cb8d2 100644 --- a/web-client/src/presenter/computeds/scanBatchPreviewerHelper.test.js +++ b/web-client/src/presenter/computeds/scanBatchPreviewerHelper.test.js @@ -1,5 +1,7 @@ -import { Scan } from '../../../../shared/src/business/entities/Scan'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + ROLES, + SCAN_MODES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; import { scanBatchPreviewerHelper as scanBatchPreviewerHelperComputed } from './scanBatchPreviewerHelper'; @@ -11,15 +13,13 @@ const state = { }, }; -const { SCAN_MODES } = Scan; - const scanBatchPreviewerHelper = withAppContextDecorator( scanBatchPreviewerHelperComputed, applicationContext, ); applicationContext.getCurrentUser = () => ({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); applicationContext.getConstants = () => ({ SCAN_MODES }); diff --git a/web-client/src/presenter/computeds/scanHelper.test.js b/web-client/src/presenter/computeds/scanHelper.test.js index 2e69ca840ed..0cbf3833fae 100644 --- a/web-client/src/presenter/computeds/scanHelper.test.js +++ b/web-client/src/presenter/computeds/scanHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; import { scanHelper as scanHelperComputed } from './scanHelper'; @@ -12,7 +12,7 @@ const scanHelper = withAppContextDecorator( describe('scanHelper', () => { it('sets hasScanFeature to `true` for `petitionsclerk` user roles', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }); const result = runCompute(scanHelper, { state: {}, @@ -22,7 +22,7 @@ describe('scanHelper', () => { it('sets hasScanFeature to `true` for `docketclerk` user roles', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }); const result = runCompute(scanHelper, { state: {}, @@ -32,7 +32,7 @@ describe('scanHelper', () => { it('sets hasScanFeature to `true` for `adc` user roles', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.adc, + role: ROLES.adc, }); const result = runCompute(scanHelper, { state: {}, @@ -42,7 +42,7 @@ describe('scanHelper', () => { it('sets hasScanFeature to `false` for `petitioner` user roles', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); const result = runCompute(scanHelper, { state: {}, @@ -52,7 +52,7 @@ describe('scanHelper', () => { it('sets hasScanFeature to `false` for `practitioner` user roles', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); const result = runCompute(scanHelper, { state: {}, @@ -62,7 +62,7 @@ describe('scanHelper', () => { it('sets hasScanFeature to `false` for `respondent` user roles', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }); const result = runCompute(scanHelper, { state: {}, diff --git a/web-client/src/presenter/computeds/selectDocumentSelectHelper.js b/web-client/src/presenter/computeds/selectDocumentSelectHelper.js deleted file mode 100644 index 8ba70e7856b..00000000000 --- a/web-client/src/presenter/computeds/selectDocumentSelectHelper.js +++ /dev/null @@ -1,21 +0,0 @@ -import { - getDocumentTypesForSelect, - getSortFunction, -} from './internalTypesHelper'; -import { state } from 'cerebral'; - -export const selectDocumentSelectHelper = (get, applicationContext) => { - const { CATEGORY_MAP } = applicationContext.getConstants(); - const searchText = get(state.screenMetadata.searchText) || ''; - - const documentTypesForSelect = getDocumentTypesForSelect(CATEGORY_MAP); - - const documentTypesForSelectSorted = documentTypesForSelect.sort( - getSortFunction(searchText), - ); - - return { - documentTypesForSelect, - documentTypesForSelectSorted, - }; -}; diff --git a/web-client/src/presenter/computeds/selectDocumentSelectHelper.test.js b/web-client/src/presenter/computeds/selectDocumentSelectHelper.test.js deleted file mode 100644 index d97c1c16342..00000000000 --- a/web-client/src/presenter/computeds/selectDocumentSelectHelper.test.js +++ /dev/null @@ -1,20 +0,0 @@ -import { applicationContext } from '../../applicationContext'; -import { runCompute } from 'cerebral/test'; -import { selectDocumentSelectHelper as selectDocumentSelectHelperComputed } from './selectDocumentSelectHelper'; -import { withAppContextDecorator } from '../../withAppContext'; - -const selectDocumentSelectHelper = withAppContextDecorator( - selectDocumentSelectHelperComputed, - applicationContext, -); - -describe('selectDocumentSelectHelper', () => { - it('returns documentTypes for select components (regular and sorted)', () => { - const result = runCompute(selectDocumentSelectHelper, { - state: {}, - }); - - expect(result.documentTypesForSelect).toBeTruthy(); - expect(result.documentTypesForSelectSorted).toBeTruthy(); - }); -}); diff --git a/web-client/src/presenter/computeds/selectDocumentTypeHelper.test.js b/web-client/src/presenter/computeds/selectDocumentTypeHelper.test.js index 58ff14eec96..e1fbbfa3bd4 100644 --- a/web-client/src/presenter/computeds/selectDocumentTypeHelper.test.js +++ b/web-client/src/presenter/computeds/selectDocumentTypeHelper.test.js @@ -1,4 +1,4 @@ -import { Document } from '../../../../shared/src/business/entities/Document'; +import { DOCUMENT_CATEGORY_MAP } from '../../../../shared/src/business/entities/EntityConstants'; import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; @@ -6,7 +6,7 @@ import { selectDocumentTypeHelper as selectDocumentTypeHelperComputed } from './ import { withAppContextDecorator } from '../../withAppContext'; // external filing events don't currently contain Nonstandard I, Nonstandard J -- but if they did ... -Document.CATEGORY_MAP['Miscellaneous'].push({ +DOCUMENT_CATEGORY_MAP['Miscellaneous'].push({ category: 'Miscellaneous', documentTitle: '[First, Second, etc.] Something to [anything]', documentType: 'Something [anything]', @@ -18,7 +18,7 @@ Document.CATEGORY_MAP['Miscellaneous'].push({ scenario: 'Nonstandard I', }); -Document.CATEGORY_MAP['Decision'].push({ +DOCUMENT_CATEGORY_MAP['Decision'].push({ category: 'Decision', documentTitle: 'Stipulated Decision Entered [judge] [anything]', documentType: 'Stipulated Decision Entered', @@ -37,7 +37,7 @@ const selectDocumentTypeHelper = withAppContextDecorator( getConstants: () => { return { ...applicationContext.getConstants(), - CATEGORY_MAP: Document.CATEGORY_MAP, + CATEGORY_MAP: DOCUMENT_CATEGORY_MAP, }; }, }, @@ -101,7 +101,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Petition', eventCode: 'P', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -112,7 +112,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Answer', eventCode: 'A', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -123,7 +123,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Proposed Stipulated Decision', eventCode: 'PSDE', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, ], @@ -170,7 +170,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Petition', eventCode: 'P', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -181,7 +181,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Answer', eventCode: 'A', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -192,7 +192,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Proposed Stipulated Decision', eventCode: 'PSDE', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, ], @@ -224,7 +224,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Petition', eventCode: 'P', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -235,7 +235,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Answer', eventCode: 'A', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -246,7 +246,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Proposed Stipulated Decision', eventCode: 'PSDE', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, ], @@ -296,7 +296,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Petition', eventCode: 'P', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -307,7 +307,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Answer', eventCode: 'A', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, { @@ -318,7 +318,7 @@ describe('selectDocumentTypeHelper', () => { documentType: 'Proposed Stipulated Decision', eventCode: 'PSDE', processingStatus: 'pending', - userId: 'petitioner', + userId: '7805d1ab-18d0-43ec-bafb-654e83405416', workItems: [], }, ], diff --git a/web-client/src/presenter/computeds/startCaseHelper.test.js b/web-client/src/presenter/computeds/startCaseHelper.test.js index db7a4b67c72..4ed847e1257 100644 --- a/web-client/src/presenter/computeds/startCaseHelper.test.js +++ b/web-client/src/presenter/computeds/startCaseHelper.test.js @@ -1,5 +1,7 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + FILING_TYPES, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { getTrialCityName } from '../computeds/formattedTrialCity'; import { runCompute } from 'cerebral/test'; @@ -14,7 +16,7 @@ const startCaseHelper = withAppContextDecorator( describe('start a case computed', () => { beforeAll(() => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.petitioner, + role: ROLES.petitioner, }); }); @@ -101,12 +103,12 @@ describe('start a case computed', () => { getTrialCityName, }, }); - expect(result.filingTypes).toEqual(Case.FILING_TYPES.petitioner); + expect(result.filingTypes).toEqual(FILING_TYPES.petitioner); }); it('returns privatePractitioner filing types if user is privatePractitioner role', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.privatePractitioner, + role: ROLES.privatePractitioner, }); const result = runCompute(startCaseHelper, { @@ -117,12 +119,12 @@ describe('start a case computed', () => { getTrialCityName, }, }); - expect(result.filingTypes).toEqual(Case.FILING_TYPES.privatePractitioner); + expect(result.filingTypes).toEqual(FILING_TYPES.privatePractitioner); }); it('returns petitioner filing types by default if user is not petitioner or privatePractitioner role', () => { applicationContext.getCurrentUser = () => ({ - role: User.ROLES.irsPractitioner, + role: ROLES.irsPractitioner, }); const result = runCompute(startCaseHelper, { @@ -133,6 +135,6 @@ describe('start a case computed', () => { getTrialCityName, }, }); - expect(result.filingTypes).toEqual(Case.FILING_TYPES.petitioner); + expect(result.filingTypes).toEqual(FILING_TYPES.petitioner); }); }); diff --git a/web-client/src/presenter/computeds/startCaseInternalContactsHelper.test.js b/web-client/src/presenter/computeds/startCaseInternalContactsHelper.test.js index bc4402219fc..e205ab92816 100644 --- a/web-client/src/presenter/computeds/startCaseInternalContactsHelper.test.js +++ b/web-client/src/presenter/computeds/startCaseInternalContactsHelper.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; import { startCaseInternalContactsHelper as startCaseInternalContactsHelperComputed } from './startCaseInternalContactsHelper'; @@ -14,9 +14,9 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, - form: { partyType: ContactFactory.PARTY_TYPES.conservator }, + form: { partyType: PARTY_TYPES.conservator }, }, }); expect(result).toMatchObject({ @@ -33,9 +33,9 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, - form: { partyType: ContactFactory.PARTY_TYPES.corporation }, + form: { partyType: PARTY_TYPES.corporation }, }, }); expect(result).toMatchObject({ @@ -51,9 +51,9 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, - form: { partyType: ContactFactory.PARTY_TYPES.custodian }, + form: { partyType: PARTY_TYPES.custodian }, }, }); expect(result).toMatchObject({ @@ -70,9 +70,9 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, - form: { partyType: ContactFactory.PARTY_TYPES.donor }, + form: { partyType: PARTY_TYPES.donor }, }, }); expect(result).toMatchObject({ @@ -87,10 +87,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, }, }, }); @@ -109,10 +109,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }, }, }); @@ -129,10 +129,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }, }, }); @@ -150,10 +150,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + partyType: PARTY_TYPES.nextFriendForIncompetentPerson, }, }, }); @@ -171,10 +171,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, }, }, }); @@ -192,10 +192,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, }, }, }); @@ -213,10 +213,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + partyType: PARTY_TYPES.partnershipOtherThanTaxMatters, }, }, }); @@ -234,10 +234,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, }, }, }); @@ -255,10 +255,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -274,10 +274,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }, }, }); @@ -299,10 +299,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }, }, }); @@ -322,10 +322,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.survivingSpouse, + partyType: PARTY_TYPES.survivingSpouse, }, }, }); @@ -343,10 +343,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, }, }, }); @@ -362,10 +362,10 @@ describe('startCaseInternalContactsHelper', () => { const result = runCompute(startCaseInternalContactsHelper, { state: { constants: { - PARTY_TYPES: ContactFactory.PARTY_TYPES, + PARTY_TYPES: PARTY_TYPES, }, form: { - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, }, }, }); diff --git a/web-client/src/presenter/computeds/startCaseInternalHelper.test.js b/web-client/src/presenter/computeds/startCaseInternalHelper.test.js index d0667fa84b8..d1b80c6bb8c 100644 --- a/web-client/src/presenter/computeds/startCaseInternalHelper.test.js +++ b/web-client/src/presenter/computeds/startCaseInternalHelper.test.js @@ -1,4 +1,4 @@ -import { ContactFactory } from '../../../../shared/src/business/entities/contacts/ContactFactory'; +import { PARTY_TYPES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; import { startCaseInternalHelper as startCaseInternalHelperComputed } from './startCaseInternalHelper'; @@ -21,7 +21,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.conservator, + partyType: PARTY_TYPES.conservator, }, }, }); @@ -33,7 +33,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); @@ -45,7 +45,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.custodian, + partyType: PARTY_TYPES.custodian, }, }, }); @@ -57,7 +57,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.donor, + partyType: PARTY_TYPES.donor, }, }, }); @@ -69,7 +69,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estate, + partyType: PARTY_TYPES.estate, }, }, }); @@ -81,7 +81,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.estateWithoutExecutor, + partyType: PARTY_TYPES.estateWithoutExecutor, }, }, }); @@ -93,7 +93,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.guardian, + partyType: PARTY_TYPES.guardian, }, }, }); @@ -105,7 +105,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForIncompetentPerson, + partyType: PARTY_TYPES.nextFriendForIncompetentPerson, }, }, }); @@ -117,7 +117,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.nextFriendForMinor, + partyType: PARTY_TYPES.nextFriendForMinor, }, }, }); @@ -129,7 +129,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipAsTaxMattersPartner, + partyType: PARTY_TYPES.partnershipAsTaxMattersPartner, }, }, }); @@ -141,7 +141,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipBBA, + partyType: PARTY_TYPES.partnershipBBA, }, }, }); @@ -153,7 +153,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.partnershipOtherThanTaxMatters, + partyType: PARTY_TYPES.partnershipOtherThanTaxMatters, }, }, }); @@ -165,7 +165,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); @@ -177,7 +177,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerDeceasedSpouse, + partyType: PARTY_TYPES.petitionerDeceasedSpouse, }, }, }); @@ -189,7 +189,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitionerSpouse, + partyType: PARTY_TYPES.petitionerSpouse, }, }, }); @@ -201,7 +201,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.survivingSpouse, + partyType: PARTY_TYPES.survivingSpouse, }, }, }); @@ -213,7 +213,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.transferee, + partyType: PARTY_TYPES.transferee, }, }, }); @@ -225,7 +225,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.trust, + partyType: PARTY_TYPES.trust, }, }, }); @@ -237,7 +237,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.corporation, + partyType: PARTY_TYPES.corporation, }, }, }); @@ -248,7 +248,7 @@ describe('case detail edit computed', () => { const result = runCompute(startCaseInternalHelper, { state: { form: { - partyType: ContactFactory.PARTY_TYPES.petitioner, + partyType: PARTY_TYPES.petitioner, }, }, }); diff --git a/web-client/src/presenter/computeds/statisticsFormHelper.js b/web-client/src/presenter/computeds/statisticsFormHelper.js index ef72b95917e..3979d1e6b7f 100644 --- a/web-client/src/presenter/computeds/statisticsFormHelper.js +++ b/web-client/src/presenter/computeds/statisticsFormHelper.js @@ -3,11 +3,9 @@ import { state } from 'cerebral'; /** * gets the statistics form helper fields * - * @param {Function} get the cerebral get function used - * for getting state.form.partyType and state.constants + * @param {Function} get the cerebral get function * @param {object} applicationContext the application context - * @returns {object} partyTypes constant, showPrimary/SecondaryContact, - * showOwnershipDisclosureStatement, and ownershipDisclosureStatementDocumentId + * @returns {object} statistics form helper fields */ export const statisticsFormHelper = (get, applicationContext) => { const { CASE_TYPES_MAP } = applicationContext.getConstants(); diff --git a/web-client/src/presenter/computeds/statisticsFormHelper.test.js b/web-client/src/presenter/computeds/statisticsFormHelper.test.js index 36554539f80..3065c6f37ff 100644 --- a/web-client/src/presenter/computeds/statisticsFormHelper.test.js +++ b/web-client/src/presenter/computeds/statisticsFormHelper.test.js @@ -1,4 +1,4 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; +import { CASE_TYPES_MAP } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { runCompute } from 'cerebral/test'; import { statisticsFormHelper as statisticsFormHelperComputed } from './statisticsFormHelper'; @@ -14,7 +14,7 @@ describe('case detail edit computed', () => { const result = runCompute(statisticsFormHelper, { state: { form: { - caseType: Case.CASE_TYPES_MAP.deficiency, + caseType: CASE_TYPES_MAP.deficiency, hasVerifiedIrsNotice: true, }, }, @@ -26,7 +26,7 @@ describe('case detail edit computed', () => { const result = runCompute(statisticsFormHelper, { state: { form: { - caseType: Case.CASE_TYPES_MAP.deficiency, + caseType: CASE_TYPES_MAP.deficiency, hasVerifiedIrsNotice: false, }, }, @@ -38,7 +38,7 @@ describe('case detail edit computed', () => { const result = runCompute(statisticsFormHelper, { state: { form: { - caseType: Case.CASE_TYPES_MAP.cdp, + caseType: CASE_TYPES_MAP.cdp, hasVerifiedIrsNotice: true, }, }, diff --git a/web-client/src/presenter/computeds/statisticsHelper.js b/web-client/src/presenter/computeds/statisticsHelper.js new file mode 100644 index 00000000000..ddccc2f0ca0 --- /dev/null +++ b/web-client/src/presenter/computeds/statisticsHelper.js @@ -0,0 +1,115 @@ +import { state } from 'cerebral'; + +export const formatStatistic = ({ applicationContext, statistic }) => { + const formattedDate = + statistic.year || + applicationContext + .getUtilities() + .formatDateString(statistic.lastDateOfPeriod, 'MMDDYY'); + + const formattedIrsDeficiencyAmount = applicationContext + .getUtilities() + .formatDollars(statistic.irsDeficiencyAmount); + + const formattedIrsTotalPenalties = applicationContext + .getUtilities() + .formatDollars(statistic.irsTotalPenalties); + + const formattedDeterminationDeficiencyAmount = statistic.determinationDeficiencyAmount + ? applicationContext + .getUtilities() + .formatDollars(statistic.determinationDeficiencyAmount) + : 'TBD'; + + const formattedDeterminationTotalPenalties = statistic.determinationTotalPenalties + ? applicationContext + .getUtilities() + .formatDollars(statistic.determinationTotalPenalties) + : 'TBD'; + + return { + ...statistic, + formattedDate, + formattedDeterminationDeficiencyAmount, + formattedDeterminationTotalPenalties, + formattedIrsDeficiencyAmount, + formattedIrsTotalPenalties, + }; +}; + +/** + * gets the formatted statistics + * + * @param {Function} get the cerebral get function + * @param {object} applicationContext the application context + * @returns {object} formatted statistics + */ +export const statisticsHelper = (get, applicationContext) => { + const { caseType, damages, litigationCosts, statistics } = get( + state.caseDetail, + ); + const permissions = get(state.permissions); + const { CASE_TYPES_MAP } = applicationContext.getConstants(); + + let formattedStatistics; + + const formatStatisticDateForSort = statistic => { + let date; + if (statistic.yearOrPeriod === 'Year') { + date = `${statistic.year}-12-31`; + } else { + date = applicationContext + .getUtilities() + .formatDateString(statistic.lastDateOfPeriod, 'YYYYMMDD'); + } + return date; + }; + + if (statistics && statistics.length > 0) { + formattedStatistics = statistics + .map(statistic => formatStatistic({ applicationContext, statistic })) + .sort((a, b) => + applicationContext + .getUtilities() + .dateStringsCompared( + formatStatisticDateForSort(a), + formatStatisticDateForSort(b), + ), + ); + } + + const formattedDamages = + damages && applicationContext.getUtilities().formatDollars(damages); + const formattedLitigationCosts = + litigationCosts && + applicationContext.getUtilities().formatDollars(litigationCosts); + + const showDamages = !!formattedDamages; + const showLitigationCosts = !!formattedLitigationCosts; + const showOtherStatistics = showDamages || showLitigationCosts; + + const hasMaxDeficiencyStatistics = statistics && statistics.length === 12; + + const showAddDeficiencyStatisticsButton = + permissions.ADD_EDIT_STATISTICS && + caseType === CASE_TYPES_MAP.deficiency && + !hasMaxDeficiencyStatistics; + + const showAddOtherStatisticsButton = + permissions.ADD_EDIT_STATISTICS && !showOtherStatistics; + + return { + formattedDamages, + formattedLitigationCosts, + formattedStatistics, + showAddButtons: + showAddDeficiencyStatisticsButton || showAddOtherStatisticsButton, + showAddDeficiencyStatisticsButton, + showAddOtherStatisticsButton, + showDamages, + showEditButtons: permissions.ADD_EDIT_STATISTICS, + showLitigationCosts, + showNoStatistics: !formattedStatistics && !showOtherStatistics, + showOtherStatistics, + }; +}; diff --git a/web-client/src/presenter/computeds/statisticsHelper.test.js b/web-client/src/presenter/computeds/statisticsHelper.test.js new file mode 100644 index 00000000000..aea8c7e47ba --- /dev/null +++ b/web-client/src/presenter/computeds/statisticsHelper.test.js @@ -0,0 +1,340 @@ +import { CASE_TYPES_MAP } from '../../../../shared/src/business/entities/EntityConstants'; +import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; +import { runCompute } from 'cerebral/test'; +import { statisticsHelper as statisticsHelperComputed } from './statisticsHelper'; +import { withAppContextDecorator } from '../../withAppContext'; + +const statisticsHelper = withAppContextDecorator( + statisticsHelperComputed, + applicationContext, +); + +describe('statisticsHelper', () => { + it('formats statistics with formatted dates and money', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + damages: 1234.56, + litigationCosts: 9, + statistics: [ + { + irsDeficiencyAmount: 123, + irsTotalPenalties: 30000, + year: '2012', + yearOrPeriod: 'Year', + }, + { + determinationDeficiencyAmount: 1234, + determinationTotalPenalties: 33.45, + irsDeficiencyAmount: 0, + irsTotalPenalties: 21, + lastDateOfPeriod: '2019-03-01T21:40:46.415Z', + yearOrPeriod: 'Period', + }, + ], + }, + permissions: {}, + }, + }); + + expect(result).toMatchObject({ + formattedDamages: '$1,234.56', + formattedLitigationCosts: '$9.00', + formattedStatistics: [ + { + formattedDate: '2012', + formattedDeterminationDeficiencyAmount: 'TBD', + formattedDeterminationTotalPenalties: 'TBD', + formattedIrsDeficiencyAmount: '$123.00', + formattedIrsTotalPenalties: '$30,000.00', + }, + { + formattedDate: '03/01/19', + formattedDeterminationDeficiencyAmount: '$1,234.00', + formattedDeterminationTotalPenalties: '$33.45', + formattedIrsDeficiencyAmount: '$0.00', + formattedIrsTotalPenalties: '$21.00', + }, + ], + }); + }); + + it('sorts formatted statistics by year / lastDateOfPeriod', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + statistics: [ + { year: 2012, yearOrPeriod: 'Year' }, + { year: 2011, yearOrPeriod: 'Year' }, + { year: 2010, yearOrPeriod: 'Year' }, + { + lastDateOfPeriod: '2019-12-02T12:00:00.000Z', + yearOrPeriod: 'Period', + }, + { year: 2013, yearOrPeriod: 'Year' }, + { + lastDateOfPeriod: '2011-11-01T12:00:00.000Z', + yearOrPeriod: 'Period', + }, + ], + }, + permissions: {}, + }, + }); + + expect(result.formattedStatistics).toMatchObject([ + { formattedDate: 2010 }, + { formattedDate: '11/01/11' }, + { formattedDate: 2011 }, + { formattedDate: 2012 }, + { formattedDate: 2013 }, + { formattedDate: '12/02/19' }, + ]); + }); + + it('returns undefined formattedStatistics if caseDetail.statistics is length 0', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + statistics: [], + }, + permissions: {}, + }, + }); + + expect(result.formattedStatistics).toBeUndefined(); + }); + + it('returns undefined values if caseDetail.statistics, damages, and litigationCosts are undefined', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: {}, + permissions: {}, + }, + }); + + expect(result).toMatchObject({ + formattedDamages: undefined, + formattedLitigationCosts: undefined, + formattedStatistics: undefined, + }); + }); + + it('returns showAddDeficiencyStatisticsButton true if permissions.ADD_EDIT_STATISTICS is true and case type is deficiency', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.deficiency, + }, + permissions: { + ADD_EDIT_STATISTICS: true, + }, + }, + }); + + expect(result).toMatchObject({ + showAddButtons: true, + showAddDeficiencyStatisticsButton: true, + }); + }); + + it('returns showAddDeficiencyStatisticsButton false if permissions.ADD_EDIT_STATISTICS is false and case type is deficiency', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.deficiency, + }, + permissions: { + ADD_EDIT_STATISTICS: false, + }, + }, + }); + + expect(result).toMatchObject({ + showAddButtons: false, + showAddDeficiencyStatisticsButton: false, + }); + }); + + it('returns showAddDeficiencyStatisticsButton false if permissions.ADD_EDIT_STATISTICS is true and case type is not deficiency', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.cdp, + }, + permissions: { + ADD_EDIT_STATISTICS: true, + }, + }, + }); + + expect(result).toMatchObject({ + showAddButtons: true, + showAddDeficiencyStatisticsButton: false, + }); + }); + + it('returns showAddDeficiencyStatisticsButton false if the maximum number of statistics for a case has been reached', () => { + const statisticsWithMaxLength = new Array(12); // 12 is the maximum number of statistics + + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + caseType: CASE_TYPES_MAP.deficiency, + statistics: statisticsWithMaxLength, + }, + permissions: { + ADD_EDIT_STATISTICS: true, + }, + }, + }); + + expect(result).toMatchObject({ + showAddButtons: true, + showAddDeficiencyStatisticsButton: false, + }); + }); + + it('returns showAddOtherStatisticsButton true if permissions.ADD_EDIT_STATISTICS is true and other statistics are not already added', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: {}, + permissions: { + ADD_EDIT_STATISTICS: true, + }, + }, + }); + + expect(result).toMatchObject({ + showAddButtons: true, + showAddOtherStatisticsButton: true, + }); + }); + + it('returns showAddOtherStatisticsButton false if permissions.ADD_EDIT_STATISTICS is true and other statistics are already added', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + litigationCosts: 1234, + }, + permissions: { + ADD_EDIT_STATISTICS: true, + }, + }, + }); + + expect(result).toMatchObject({ + showAddButtons: false, + showAddOtherStatisticsButton: false, + }); + }); + + it('returns showEditButtons false if permissions.ADD_EDIT_STATISTICS is false', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: {}, + permissions: { + ADD_EDIT_STATISTICS: false, + }, + }, + }); + + expect(result.showEditButtons).toEqual(false); + }); + + it('returns showEditButtons true if permissions.ADD_EDIT_STATISTICS is true', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: {}, + permissions: { + ADD_EDIT_STATISTICS: true, + }, + }, + }); + + expect(result.showEditButtons).toEqual(true); + }); + + it('returns showNoStatistics true if there are no statistics on the case', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: {}, + permissions: {}, + }, + }); + + expect(result.showNoStatistics).toEqual(true); + }); + + it('returns showNoStatistics false if there are statistics on the case', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + statistics: [ + { + irsDeficiencyAmount: 123, + irsTotalPenalties: 30000, + year: '2012', + yearOrPeriod: 'Year', + }, + ], + }, + permissions: {}, + }, + }); + + expect(result.showNoStatistics).toEqual(false); + }); + + it('returns showNoStatistics false if there are damages on the case', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + damages: 1234, + }, + permissions: {}, + }, + }); + + expect(result.showNoStatistics).toEqual(false); + }); + + it('returns showOtherStatistics false if there are no damages or litigationCosts on the case', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: {}, + permissions: {}, + }, + }); + + expect(result.showOtherStatistics).toEqual(false); + }); + + it('returns showDamages and showOtherStatistics true if there are damages on the case', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + damages: 1234, + }, + permissions: {}, + }, + }); + + expect(result.showOtherStatistics).toEqual(true); + expect(result.showDamages).toEqual(true); + }); + + it('returns showLitigationCosts showOtherStatistics true if there are damages on the case', () => { + const result = runCompute(statisticsHelper, { + state: { + caseDetail: { + litigationCosts: 1234, + }, + permissions: {}, + }, + }); + + expect(result.showOtherStatistics).toEqual(true); + expect(result.showLitigationCosts).toEqual(true); + }); +}); diff --git a/web-client/src/presenter/computeds/trialSessionHeaderHelper.test.js b/web-client/src/presenter/computeds/trialSessionHeaderHelper.test.js index 89e14f7f11e..bef10694fdf 100644 --- a/web-client/src/presenter/computeds/trialSessionHeaderHelper.test.js +++ b/web-client/src/presenter/computeds/trialSessionHeaderHelper.test.js @@ -1,6 +1,6 @@ +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { User } from '../../../../shared/src/business/entities/User'; import { runCompute } from 'cerebral/test'; - import { trialSessionHeaderHelper as trialSessionHeaderHelperComputed } from './trialSessionHeaderHelper'; import { withAppContextDecorator } from '../../withAppContext'; @@ -14,7 +14,7 @@ const trialSessionHeaderHelper = withAppContextDecorator( trialSessionHeaderHelperComputed, { getConstants: () => ({ - USER_ROLES: User.ROLES, + USER_ROLES: ROLES, }), getCurrentUser: () => currentUser, getUtilities: () => ({ @@ -29,25 +29,25 @@ const trialSessionHeaderHelper = withAppContextDecorator( const chambersUser = new User({ name: 'Trial Judge Chambers', - role: User.ROLES.chambers, + role: ROLES.chambers, userId: CHAMBERS_USER_ID, }); const judgeUser = new User({ name: 'Trial Judge', - role: User.ROLES.judge, + role: ROLES.judge, userId: JUDGE_USER_ID, }); const trialClerkUser = new User({ name: 'Trial Clerk', - role: User.ROLES.trialClerk, + role: ROLES.trialClerk, userId: TRIAL_CLERK_USER_ID, }); const baseState = { - constants: { USER_ROLES: User.ROLES }, - judgeUser: { role: User.ROLES.judge, userId: JUDGE_USER_ID }, + constants: { USER_ROLES: ROLES }, + judgeUser: { role: ROLES.judge, userId: JUDGE_USER_ID }, }; describe('trial session helper computed', () => { diff --git a/web-client/src/presenter/computeds/trialSessionWorkingCopyHelper.test.js b/web-client/src/presenter/computeds/trialSessionWorkingCopyHelper.test.js index 9ed2a4d6d21..fb7008e6baa 100644 --- a/web-client/src/presenter/computeds/trialSessionWorkingCopyHelper.test.js +++ b/web-client/src/presenter/computeds/trialSessionWorkingCopyHelper.test.js @@ -1,6 +1,9 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; +import { + CASE_STATUS_TYPES, + STATUS_TYPES, + TRIAL_STATUS_TYPES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { MOCK_CASE } from '../../../../shared/src/test/mockCase'; -import { TrialSessionWorkingCopy } from '../../../../shared/src/business/entities/trialSessions/TrialSessionWorkingCopy'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; import { trialSessionWorkingCopyHelper as trialSessionWorkingCopyHelperComputed } from './trialSessionWorkingCopyHelper'; @@ -363,22 +366,22 @@ describe('trial session working copy computed', () => { { ...MOCK_CASE, docketNumber: '102-19', - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, { ...MOCK_CASE, docketNumber: '5000-17', - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, { ...MOCK_CASE, docketNumber: '500-17', - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, { ...MOCK_CASE, docketNumber: '90-07', - status: Case.STATUS_TYPES.closed, + status: CASE_STATUS_TYPES.closed, }, ], }, @@ -399,8 +402,8 @@ describe('trial session working copy computed', () => { let result = runCompute(trialSessionWorkingCopyHelper, { state: { constants: { - STATUS_TYPES: Case.STATUS_TYPES, - TRIAL_STATUS_TYPES: TrialSessionWorkingCopy.TRIAL_STATUS_TYPES, + STATUS_TYPES, + TRIAL_STATUS_TYPES, }, trialSession: { ...TRIAL_SESSION, diff --git a/web-client/src/presenter/computeds/trialSessionsHelper.test.js b/web-client/src/presenter/computeds/trialSessionsHelper.test.js index ad5dc2feeec..8c155bbe759 100644 --- a/web-client/src/presenter/computeds/trialSessionsHelper.test.js +++ b/web-client/src/presenter/computeds/trialSessionsHelper.test.js @@ -1,11 +1,11 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { runCompute } from 'cerebral/test'; import { trialSessionsHelper as trialSessionsHelperComputed } from './trialSessionsHelper'; import { withAppContextDecorator } from '../../withAppContext'; let currentUser = { - role: User.ROLES.judge, - userId: '777', + role: ROLES.judge, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const trialSessionsHelper = withAppContextDecorator( diff --git a/web-client/src/presenter/computeds/trialSessionsSummaryHelper.test.js b/web-client/src/presenter/computeds/trialSessionsSummaryHelper.test.js index 9d6c641e3d1..af830d58cf1 100644 --- a/web-client/src/presenter/computeds/trialSessionsSummaryHelper.test.js +++ b/web-client/src/presenter/computeds/trialSessionsSummaryHelper.test.js @@ -1,15 +1,18 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { runCompute } from 'cerebral/test'; import { trialSessionsSummaryHelper as trialSessionsSummaryHelperComputed } from './trialSessionsSummaryHelper'; import { withAppContextDecorator } from '../../withAppContext'; let currentUser; +const judgeId = '733777c2-cb31-44c6-afce-f7bc1a3f613b'; +const chambersId = 'a5ac202c-4e96-46f2-8968-2749ea845143'; + const trialSessionsSummaryHelper = withAppContextDecorator( trialSessionsSummaryHelperComputed, { getConstants: () => ({ - USER_ROLES: User.ROLES, + USER_ROLES: ROLES, }), getCurrentUser: () => currentUser, }, @@ -18,8 +21,8 @@ const trialSessionsSummaryHelper = withAppContextDecorator( describe('trialSessionsSummaryHelper', () => { beforeEach(() => { currentUser = { - role: User.ROLES.judge, - userId: '777', + role: ROLES.judge, + userId: judgeId, }; }); @@ -28,24 +31,24 @@ describe('trialSessionsSummaryHelper', () => { state: {}, }); - expect(result.judgeUserId).toEqual('777'); + expect(result.judgeUserId).toEqual(judgeId); }); it('should return the judeUserId as the chambers judge associated with the logged in user', () => { currentUser = { - role: User.ROLES.chambers, - userId: '444', + role: ROLES.chambers, + userId: chambersId, }; const result = runCompute(trialSessionsSummaryHelper, { state: { judgeUser: { - role: User.ROLES.judge, - userId: '777', + role: ROLES.judge, + userId: judgeId, }, }, }); - expect(result.judgeUserId).toEqual('777'); + expect(result.judgeUserId).toEqual(judgeId); }); }); diff --git a/web-client/src/presenter/computeds/updateCaseModalHelper.test.js b/web-client/src/presenter/computeds/updateCaseModalHelper.test.js index 10cbcf1ecef..a8ed5fa8102 100644 --- a/web-client/src/presenter/computeds/updateCaseModalHelper.test.js +++ b/web-client/src/presenter/computeds/updateCaseModalHelper.test.js @@ -1,4 +1,7 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; +import { + CASE_STATUS_TYPES, + STATUS_TYPES_MANUAL_UPDATE, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { runCompute } from 'cerebral/test'; import { updateCaseModalHelper as updateCaseModalHelperComputed } from './updateCaseModalHelper'; @@ -15,7 +18,7 @@ describe('updateCaseModalHelper', () => { beforeEach(() => { mockCase = { caseId: '123', - status: Case.STATUS_TYPES.new, + status: CASE_STATUS_TYPES.new, }; }); @@ -24,7 +27,7 @@ describe('updateCaseModalHelper', () => { state: { caseDetail: mockCase, modal: { - caseStatus: Case.STATUS_TYPES.submitted, + caseStatus: CASE_STATUS_TYPES.submitted, }, }, }); @@ -36,7 +39,7 @@ describe('updateCaseModalHelper', () => { state: { caseDetail: mockCase, modal: { - caseStatus: Case.STATUS_TYPES.new, + caseStatus: CASE_STATUS_TYPES.new, }, }, }); @@ -46,7 +49,7 @@ describe('updateCaseModalHelper', () => { it('returns showCalendaredAlert true if the case is currently calendared', () => { const result = runCompute(updateCaseModalHelper, { state: { - caseDetail: { ...mockCase, status: Case.STATUS_TYPES.calendared }, + caseDetail: { ...mockCase, status: CASE_STATUS_TYPES.calendared }, }, }); expect(result.showCalendaredAlert).toBeTruthy(); @@ -58,6 +61,6 @@ describe('updateCaseModalHelper', () => { caseDetail: mockCase, }, }); - expect(result.caseStatusOptions).toEqual(Case.STATUS_TYPES_MANUAL_UPDATE); + expect(result.caseStatusOptions).toEqual(STATUS_TYPES_MANUAL_UPDATE); }); }); diff --git a/web-client/src/presenter/computeds/workQueueHelper.test.js b/web-client/src/presenter/computeds/workQueueHelper.test.js index 9d39f54ca1a..ea56b6a618a 100644 --- a/web-client/src/presenter/computeds/workQueueHelper.test.js +++ b/web-client/src/presenter/computeds/workQueueHelper.test.js @@ -1,4 +1,4 @@ -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContext } from '../../applicationContext'; import { getUserPermissions } from '../../../../shared/src/authorization/getUserPermissions'; import { runCompute } from 'cerebral/test'; @@ -24,8 +24,8 @@ const getBaseState = user => { describe('workQueueHelper', () => { it('returns the expected state when selected work items are set', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -50,8 +50,8 @@ describe('workQueueHelper', () => { it('returns the expected state when selected work items are not set', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -76,8 +76,8 @@ describe('workQueueHelper', () => { it('returns My Messages for workQueueTitle if showing individual internal work queue', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -100,8 +100,8 @@ describe('workQueueHelper', () => { it('returns Section Messages for workQueueTitle if showing section internal work queue', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -124,8 +124,8 @@ describe('workQueueHelper', () => { it('returns My Document QC for workQueueTitle if showing individual non-internal work queue', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -148,8 +148,8 @@ describe('workQueueHelper', () => { it('returns Document QC for workQueueTitle if showing section non-internal work queue and current user is not a docket or petitions clerk', () => { const user = { - role: User.ROLES.adc, - userId: '123', + role: ROLES.adc, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -172,8 +172,8 @@ describe('workQueueHelper', () => { it('returns Section Document QC for workQueueTitle if showing section non-internal work queue and current user is a docket clerk', () => { const user = { - role: User.ROLES.docketClerk, - userId: '123', + role: ROLES.docketClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -196,8 +196,8 @@ describe('workQueueHelper', () => { it('shows the start a case button when role is petitions clerk', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -217,8 +217,8 @@ describe('workQueueHelper', () => { it('does not show the start a case button when role is docket clerk', () => { const user = { - role: User.ROLES.docketClerk, - userId: '123', + role: ROLES.docketClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -238,8 +238,8 @@ describe('workQueueHelper', () => { it('shows the case status column when role is judge', () => { const user = { - role: User.ROLES.judge, - userId: '123', + role: ROLES.judge, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -257,8 +257,8 @@ describe('workQueueHelper', () => { it('shows the case status column when role is chambers', () => { const user = { - role: User.ROLES.chambers, - userId: '123', + role: ROLES.chambers, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -276,8 +276,8 @@ describe('workQueueHelper', () => { it('shows the from column when role is judge', () => { const user = { - role: User.ROLES.judge, - userId: '123', + role: ROLES.judge, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -295,8 +295,8 @@ describe('workQueueHelper', () => { it('shows the from column when role is chambers', () => { const user = { - role: User.ROLES.chambers, - userId: '123', + role: ROLES.chambers, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -314,8 +314,8 @@ describe('workQueueHelper', () => { it('shows "Received" as filed label on messages inbox', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { @@ -337,8 +337,8 @@ describe('workQueueHelper', () => { it('shows in progress petitions for a petitionsclerk', () => { const user = { - role: User.ROLES.petitionsClerk, - userId: '123', + role: ROLES.petitionsClerk, + userId: '9d7fd667-42a4-4bd0-9ec7-89d2673cf8b1', }; const result = runCompute(workQueueHelper, { state: { diff --git a/web-client/src/presenter/errors/ErrorFactory.js b/web-client/src/presenter/errors/ErrorFactory.js index 62322197e9a..6908d5aeb86 100644 --- a/web-client/src/presenter/errors/ErrorFactory.js +++ b/web-client/src/presenter/errors/ErrorFactory.js @@ -22,7 +22,6 @@ export const ErrorFactory = { newError = new ServerInvalidResponseError(e); } else if (!e.response) { // this should only happen if cognito throws a cors exception due to expired tokens or invalid tokens - console.log('e', e); newError = new UnidentifiedUserError(e); } newError.originalError = e; diff --git a/web-client/src/presenter/presenter-public.js b/web-client/src/presenter/presenter-public.js index 10c7077c9d3..cc29279a380 100644 --- a/web-client/src/presenter/presenter-public.js +++ b/web-client/src/presenter/presenter-public.js @@ -7,12 +7,14 @@ import { clearPdfPreviewUrlSequence } from './sequences/clearPdfPreviewUrlSequen import { gotoPublicCaseDetailSequence } from './sequences/public/gotoPublicCaseDetailSequence'; import { gotoPublicPrintableDocketRecordSequence } from './sequences/public/gotoPublicPrintableDocketRecordSequence'; import { gotoPublicSearchSequence } from './sequences/public/gotoPublicSearchSequence'; +import { gotoTodaysOpinionsSequence } from './sequences/public/gotoTodaysOpinionsSequence'; import { navigateBackSequence } from './sequences/navigateBackSequence'; import { navigateToCognitoSequence } from './sequences/navigateToCognitoSequence'; import { navigateToPublicSiteSequence } from './sequences/public/navigateToPublicSiteSequence'; +import { notFoundErrorSequence } from './sequences/notFoundErrorSequence'; import { showMoreResultsSequence } from './sequences/showMoreResultsSequence'; -import { submitCaseDocketNumberSearchSequence } from './sequences/submitCaseDocketNumberSearchSequence'; import { submitPublicCaseAdvancedSearchSequence } from './sequences/public/submitPublicCaseAdvancedSearchSequence'; +import { submitPublicCaseDocketNumberSearchSequence } from './sequences/public/submitPublicCaseDocketNumberSearchSequence'; import { submitPublicOpinionAdvancedSearchSequence } from './sequences/public/submitPublicOpinionAdvancedSearchSequence'; import { submitPublicOrderAdvancedSearchSequence } from './sequences/public/submitPublicOrderAdvancedSearchSequence'; import { toggleBetaBarSequence } from './sequences/toggleBetaBarSequence'; @@ -22,6 +24,7 @@ import { updateAdvancedOrderSearchFormValueSequence } from './sequences/updateAd import { updateAdvancedSearchFormValueSequence } from './sequences/updateAdvancedSearchFormValueSequence'; import { updateDocketNumberSearchFormSequence } from './sequences/updateDocketNumberSearchFormSequence'; import { validateCaseAdvancedSearchFormSequence } from './sequences/validateCaseAdvancedSearchFormSequence'; +import { validateCaseDocketNumberSearchFormSequence } from './sequences/validateCaseDocketNumberSearchFormSequence'; import { validateOpinionSearchSequence } from './sequences/validateOpinionSearchSequence'; import { validateOrderSearchSequence } from './sequences/validateOrderSearchSequence'; @@ -35,12 +38,14 @@ export const presenter = { gotoPublicCaseDetailSequence, gotoPublicPrintableDocketRecordSequence, gotoPublicSearchSequence, + gotoTodaysOpinionsSequence, navigateBackSequence, navigateToCognitoSequence, navigateToPublicSiteSequence, + notFoundErrorSequence, showMoreResultsSequence, - submitCaseDocketNumberSearchSequence, submitPublicCaseAdvancedSearchSequence: submitPublicCaseAdvancedSearchSequence, + submitPublicCaseDocketNumberSearchSequence, submitPublicOpinionAdvancedSearchSequence, submitPublicOrderAdvancedSearchSequence, toggleBetaBarSequence, @@ -50,6 +55,7 @@ export const presenter = { updateAdvancedSearchFormValueSequence, updateDocketNumberSearchFormSequence, validateCaseAdvancedSearchFormSequence, + validateCaseDocketNumberSearchFormSequence, validateOpinionSearchSequence, validateOrderSearchSequence, }, diff --git a/web-client/src/presenter/presenter.js b/web-client/src/presenter/presenter.js index 19f348aa9c7..7492323a7b2 100644 --- a/web-client/src/presenter/presenter.js +++ b/web-client/src/presenter/presenter.js @@ -19,9 +19,11 @@ import { batchDownloadErrorSequence } from './sequences/batchDownloadErrorSequen import { batchDownloadReadySequence } from './sequences/batchDownloadReadySequence'; import { batchDownloadTrialSessionSequence } from './sequences/batchDownloadTrialSessionSequence'; import { blockCaseFromTrialSequence } from './sequences/blockCaseFromTrialSequence'; +import { calculatePenaltiesForAddSequence } from './sequences/calculatePenaltiesForAddSequence'; import { calculatePenaltiesSequence } from './sequences/calculatePenaltiesSequence'; import { cancelAddDraftDocumentSequence } from './sequences/cancelAddDraftDocumentSequence'; -import { cancelEditPrimaryContactSequence } from './sequences/cancelEditPrimaryContactSequence'; +import { cancelAddStatisticSequence } from './sequences/cancelAddStatisticSequence'; +import { cancelAndNavigateToCorrespondenceSequence } from './sequences/cancelAndNavigateToCorrespondenceSequence'; import { cancelFileUploadSequence } from './sequences/cancelFileUploadSequence'; import { caseInventoryReportLoadMoreSequence } from './sequences/caseInventoryReportLoadMoreSequence'; import { cerebralBindSimpleSetStateSequence } from './sequences/cerebralBindSimpleSetStateSequence'; @@ -36,7 +38,6 @@ import { clearModalFormSequence } from './sequences/clearModalFormSequence'; import { clearModalSequence } from './sequences/clearModalSequence'; import { clearPdfPreviewUrlSequence } from './sequences/clearPdfPreviewUrlSequence'; import { clearPreferredTrialCitySequence } from './sequences/clearPreferredTrialCitySequence'; -import { clearWizardDataSequence } from './sequences/clearWizardDataSequence'; import { closeModalAndReturnToCaseDetailDraftDocumentsSequence } from './sequences/closeModalAndReturnToCaseDetailDraftDocumentsSequence'; import { closeModalAndReturnToCaseDetailSequence } from './sequences/closeModalAndReturnToCaseDetailSequence'; import { closeModalAndReturnToDashboardSequence } from './sequences/closeModalAndReturnToDashboardSequence'; @@ -51,21 +52,23 @@ import { contactPrimaryCountryTypeChangeSequence } from './sequences/contactPrim import { contactSecondaryCountryTypeChangeSequence } from './sequences/contactSecondaryCountryTypeChangeSequence'; import { convertHtml2PdfSequence } from './sequences/convertHtml2PdfSequence'; import { copyPrimaryContactSequence } from './sequences/copyPrimaryContactSequence'; -import { countryTypeFormContactChangeSequence } from './sequences/countryTypeFormContactChangeSequence'; import { countryTypeUserContactChangeSequence } from './sequences/countryTypeUserContactChangeSequence'; import { createCaseDeadlineSequence } from './sequences/createCaseDeadlineSequence'; +import { createCaseMessageSequence } from './sequences/createCaseMessageSequence'; import { createWorkItemSequence } from './sequences/createWorkItemSequence'; import { deleteCaseDeadlineSequence } from './sequences/deleteCaseDeadlineSequence'; import { deleteCaseNoteSequence } from './sequences/deleteCaseNoteSequence'; +import { deleteCorrespondenceDocumentSequence } from './sequences/deleteCorrespondenceDocumentSequence'; +import { deleteDeficiencyStatisticsSequence } from './sequences/deleteDeficiencyStatisticsSequence'; import { deleteJudgesCaseNoteFromCaseDetailSequence } from './sequences/deleteJudgesCaseNoteFromCaseDetailSequence'; +import { deleteOtherStatisticsSequence } from './sequences/deleteOtherStatisticsSequence'; import { deleteTrialSessionSequence } from './sequences/deleteTrialSessionSequence'; import { deleteUserCaseNoteFromWorkingCopySequence } from './sequences/deleteUserCaseNoteFromWorkingCopySequence'; import { deleteWorkingCopySessionNoteSequence } from './sequences/deleteWorkingCopySessionNoteSequence'; import { dismissAlertSequence } from './sequences/dismissAlertSequence'; import { dismissCreateMessageModalSequence } from './sequences/dismissCreateMessageModalSequence'; import { dismissModalSequence } from './sequences/dismissModalSequence'; -import { editSelectedDocumentSequence } from './sequences/editSelectedDocumentSequence'; -import { editSelectedSecondaryDocumentSequence } from './sequences/editSelectedSecondaryDocumentSequence'; +import { editCorrespondenceDocumentSequence } from './sequences/editCorrespondenceDocumentSequence'; import { editUploadCourtIssuedDocumentSequence } from './sequences/editUploadCourtIssuedDocumentSequence'; import { fetchPendingItemsSequence } from './sequences/pending/fetchPendingItemsSequence'; import { fetchUserNotificationsSequence } from './sequences/fetchUserNotificationsSequence'; @@ -74,11 +77,13 @@ import { generateCaseCaptionSequence } from './sequences/generateCaseCaptionSequ import { generatePdfFromScanSessionSequence } from './sequences/generatePdfFromScanSessionSequence'; import { getBlockedCasesByTrialLocationSequence } from './sequences/getBlockedCasesByTrialLocationSequence'; import { getCaseInventoryReportSequence } from './sequences/getCaseInventoryReportSequence'; +import { getCasesByStatusForUserSequence } from './sequences/getCasesByStatusForUserSequence'; import { getUsersInSectionSequence } from './sequences/getUsersInSectionSequence'; -import { goBackToStartCaseInternalSequence } from './sequences/goBackToStartCaseInternalSequence'; import { gotoAccessibilityStatementSequence } from './sequences/gotoAccessibilityStatementSequence'; import { gotoAddCourtIssuedDocketEntrySequence } from './sequences/gotoAddCourtIssuedDocketEntrySequence'; +import { gotoAddDeficiencyStatisticsSequence } from './sequences/gotoAddDeficiencyStatisticsSequence'; import { gotoAddDocketEntrySequence } from './sequences/gotoAddDocketEntrySequence'; +import { gotoAddOtherStatisticsSequence } from './sequences/gotoAddOtherStatisticsSequence'; import { gotoAddTrialSessionSequence } from './sequences/gotoAddTrialSessionSequence'; import { gotoAdvancedSearchSequence } from './sequences/gotoAdvancedSearchSequence'; import { gotoAllCaseDeadlinesSequence } from './sequences/gotoAllCaseDeadlinesSequence'; @@ -87,26 +92,30 @@ import { gotoBeforeYouFileDocumentSequence } from './sequences/gotoBeforeYouFile import { gotoBlockedCasesReportSequence } from './sequences/gotoBlockedCasesReportSequence'; import { gotoCaseDetailSequence } from './sequences/gotoCaseDetailSequence'; import { gotoCaseInventoryReportSequence } from './sequences/gotoCaseInventoryReportSequence'; +import { gotoCaseMessagesSequence } from './sequences/gotoCaseMessagesSequence'; import { gotoCaseSearchNoMatchesSequence } from './sequences/gotoCaseSearchNoMatchesSequence'; import { gotoCompleteDocketEntrySequence } from './sequences/gotoCompleteDocketEntrySequence'; import { gotoCreateOrderSequence } from './sequences/gotoCreateOrderSequence'; import { gotoCreatePractitionerUserSequence } from './sequences/gotoCreatePractitionerUserSequence'; import { gotoDashboardSequence } from './sequences/gotoDashboardSequence'; import { gotoDocumentDetailSequence } from './sequences/gotoDocumentDetailSequence'; +import { gotoEditCorrespondenceDocumentSequence } from './sequences/gotoEditCorrespondenceDocumentSequence'; import { gotoEditCourtIssuedDocketEntrySequence } from './sequences/gotoEditCourtIssuedDocketEntrySequence'; +import { gotoEditDeficiencyStatisticSequence } from './sequences/gotoEditDeficiencyStatisticSequence'; import { gotoEditDocketEntryMetaSequence } from './sequences/gotoEditDocketEntryMetaSequence'; import { gotoEditDocketEntrySequence } from './sequences/gotoEditDocketEntrySequence'; import { gotoEditOrderSequence } from './sequences/gotoEditOrderSequence'; +import { gotoEditOtherStatisticsSequence } from './sequences/gotoEditOtherStatisticsSequence'; import { gotoEditPetitionDetailsSequence } from './sequences/gotoEditPetitionDetailsSequence'; import { gotoEditPetitionerInformationSequence } from './sequences/gotoEditPetitionerInformationSequence'; import { gotoEditPractitionerUserSequence } from './sequences/gotoEditPractitionerUserSequence'; -import { gotoEditSavedPetitionSequence } from './sequences/gotoEditSavedPetitionSequence'; import { gotoEditTrialSessionSequence } from './sequences/gotoEditTrialSessionSequence'; import { gotoEditUploadCourtIssuedDocumentSequence } from './sequences/gotoEditUploadCourtIssuedDocumentSequence'; import { gotoFileDocumentSequence } from './sequences/gotoFileDocumentSequence'; import { gotoFilePetitionSuccessSequence } from './sequences/gotoFilePetitionSuccessSequence'; import { gotoIdleLogoutSequence } from './sequences/gotoIdleLogoutSequence'; import { gotoLoginSequence } from './sequences/gotoLoginSequence'; +import { gotoMessageDetailSequence } from './sequences/gotoMessageDetailSequence'; import { gotoMessagesSequence } from './sequences/gotoMessagesSequence'; import { gotoPdfPreviewSequence } from './sequences/gotoPdfPreviewSequence'; import { gotoPendingReportSequence } from './sequences/gotoPendingReportSequence'; @@ -122,7 +131,6 @@ import { gotoPrintablePendingReportSequence } from './sequences/gotoPrintablePen import { gotoRequestAccessSequence } from './sequences/gotoRequestAccessSequence'; import { gotoReviewSavedPetitionSequence } from './sequences/gotoReviewSavedPetitionSequence'; import { gotoSecondaryContactEditSequence } from './sequences/gotoSecondaryContactEditSequence'; -import { gotoSelectDocumentTypeSequence } from './sequences/gotoSelectDocumentTypeSequence'; import { gotoSignOrderSequence } from './sequences/gotoSignOrderSequence'; import { gotoSignPDFDocumentSequence } from './sequences/gotoSignPDFDocumentSequence'; import { gotoStartCaseWizardSequence } from './sequences/gotoStartCaseWizardSequence'; @@ -149,7 +157,6 @@ import { navigateToPathSequence } from './sequences/navigateToPathSequence'; import { navigateToPrintPaperServiceSequence } from './sequences/navigateToPrintPaperServiceSequence'; import { navigateToPrintableCaseConfirmationSequence } from './sequences/navigateToPrintableCaseConfirmationSequence'; import { navigateToPrintableDocketRecordSequence } from './sequences/navigateToPrintableDocketRecordSequence'; -import { navigateToReviewSavedPetitionSequence } from './sequences/navigateToReviewSavedPetitionSequence'; import { notFoundErrorSequence } from './sequences/notFoundErrorSequence'; import { noticeGenerationCompleteSequence } from './sequences/noticeGenerationCompleteSequence'; import { openAddEditCaseNoteModalSequence } from './sequences/openAddEditCaseNoteModalSequence'; @@ -168,6 +175,8 @@ import { openCleanModalSequence } from './sequences/openCleanModalSequence'; import { openCompleteSelectDocumentTypeModalSequence } from './sequences/openCompleteSelectDocumentTypeModalSequence'; import { openConfirmDeleteBatchModalSequence } from './sequences/openConfirmDeleteBatchModalSequence'; import { openConfirmDeleteCorrespondenceModalSequence } from './sequences/openConfirmDeleteCorrespondenceModalSequence'; +import { openConfirmDeleteDeficiencyStatisticsModalSequence } from './sequences/openConfirmDeleteDeficiencyStatisticsModalSequence'; +import { openConfirmDeleteOtherStatisticsModalSequence } from './sequences/openConfirmDeleteOtherStatisticsModalSequence'; import { openConfirmDeletePDFModalSequence } from './sequences/openConfirmDeletePDFModalSequence'; import { openConfirmDeleteTrialSessionModalSequence } from './sequences/openConfirmDeleteTrialSessionModalSequence'; import { openConfirmEditModalSequence } from './sequences/openConfirmEditModalSequence'; @@ -176,6 +185,7 @@ import { openConfirmRemoveCaseDetailPendingItemModalSequence } from './sequences import { openConfirmRescanBatchModalSequence } from './sequences/openConfirmRescanBatchModalSequence'; import { openConfirmServeToIrsModalSequence } from './sequences/openConfirmServeToIrsModalSequence'; import { openCreateCaseDeadlineModalSequence } from './sequences/openCreateCaseDeadlineModalSequence'; +import { openCreateCaseMessageModalSequence } from './sequences/openCreateCaseMessageModalSequence'; import { openCreateMessageAlongsideDocketRecordQCModalSequence } from './sequences/openCreateMessageAlongsideDocketRecordQCModalSequence'; import { openCreateMessageModalSequence } from './sequences/openCreateMessageModalSequence'; import { openCreateOrderChooseTypeModalSequence } from './sequences/openCreateOrderChooseTypeModalSequence'; @@ -200,7 +210,6 @@ import { printDocketRecordSequence } from './sequences/printDocketRecordSequence import { printTrialCalendarSequence } from './sequences/printTrialCalendarSequence'; import { prioritizeCaseSequence } from './sequences/prioritizeCaseSequence'; import { redirectToLoginSequence } from './sequences/redirectToLoginSequence'; -import { refreshCaseSequence } from './sequences/refreshCaseSequence'; import { refreshPdfSequence } from './sequences/refreshPdfSequence'; import { refreshStatisticsSequence } from './sequences/refreshStatisticsSequence'; import { removeBatchSequence } from './sequences/removeBatchSequence'; @@ -218,7 +227,6 @@ import { reviewRequestAccessInformationSequence } from './sequences/reviewReques import { runTrialSessionPlanningReportSequence } from './sequences/runTrialSessionPlanningReportSequence'; import { saveDocumentSigningSequence } from './sequences/saveDocumentSigningSequence'; import { saveSavedCaseForLaterSequence } from './sequences/saveSavedCaseForLaterSequence'; -import { scannerShutdownSequence } from './sequences/scannerShutdownSequence'; import { scannerStartupSequence } from './sequences/scannerStartupSequence'; import { sealCaseSequence } from './sequences/sealCaseSequence'; import { selectAssigneeSequence } from './sequences/selectAssigneeSequence'; @@ -226,30 +234,25 @@ import { selectDateRangeFromCalendarSequence } from './sequences/selectDateRange import { selectDocumentForPetitionQcPreviewSequence } from './sequences/selectDocumentForPetitionQcPreviewSequence'; import { selectDocumentForPreviewSequence } from './sequences/selectDocumentForPreviewSequence'; import { selectDocumentForScanSequence } from './sequences/selectDocumentForScanSequence'; -import { selectDocumentSequence } from './sequences/selectDocumentSequence'; import { selectScannerSequence } from './sequences/selectScannerSequence'; -import { selectSecondaryDocumentSequence } from './sequences/selectSecondaryDocumentSequence'; import { selectWorkItemSequence } from './sequences/selectWorkItemSequence'; import { serveCaseToIrsSequence } from './sequences/serveCaseToIrsSequence'; import { serveCourtIssuedDocumentSequence } from './sequences/serveCourtIssuedDocumentSequence'; -import { setCanvasForPDFSigningSequence } from './sequences/setCanvasForPDFSigningSequence'; import { setCaseDetailPageTabSequence } from './sequences/setCaseDetailPageTabSequence'; import { setCaseDetailPrimaryTabSequence } from './sequences/setCaseDetailPrimaryTabSequence'; +import { setCaseTypeToDisplaySequence } from './sequences/setCaseTypeToDisplaySequence'; import { setCurrentPageErrorSequence } from './sequences/setCurrentPageErrorSequence'; import { setCurrentPageIndexSequence } from './sequences/setCurrentPageIndexSequence'; -import { setDocumentDetailTabSequence } from './sequences/setDocumentDetailTabSequence'; import { setDocumentForUploadSequence } from './sequences/setDocumentForUploadSequence'; import { setDocumentUploadModeSequence } from './sequences/setDocumentUploadModeSequence'; import { setFieldOrderSequence } from './sequences/setFieldOrderSequence'; import { setFocusedWorkItemSequence } from './sequences/setFocusedWorkItemSequence'; import { setIdleStatusIdleSequence } from './sequences/setIdleStatusIdleSequence'; import { setIrsNoticeFalseSequence } from './sequences/setIrsNoticeFalseSequence'; -import { setModalDialogNameSequence } from './sequences/setModalDialogNameSequence'; import { setPDFPageForSigningSequence } from './sequences/setPDFPageForSigningSequence'; import { setPDFSignatureDataSequence } from './sequences/setPDFSignatureDataSequence'; import { setPageSequence } from './sequences/PDFPreviewModal/setPageSequence'; import { setPdfPreviewUrlSequence } from './sequences/setPdfPreviewUrlSequence'; -import { setScannerSourceSequence } from './sequences/setScannerSourceSequence'; import { setSelectedBatchIndexSequence } from './sequences/setSelectedBatchIndexSequence'; import { setSelectedCasesForConsolidatedCaseDocumentSubmissionSequence } from './sequences/setSelectedCasesForConsolidatedCaseDocumentSubmissionSequence'; import { setTrialSessionCalendarSequence } from './sequences/setTrialSessionCalendarSequence'; @@ -257,11 +260,15 @@ import { setWorkItemActionSequence } from './sequences/setWorkItemActionSequence import { setWorkQueueIsInternalSequence } from './sequences/setWorkQueueIsInternalSequence'; import { showCalculatePenaltiesModalSequence } from './sequences/showCalculatePenaltiesModalSequence'; import { showDocketRecordDetailModalSequence } from './sequences/showDocketRecordDetailModalSequence'; +import { showMoreClosedCasesSequence } from './sequences/showMoreClosedCasesSequence'; +import { showMoreOpenCasesSequence } from './sequences/showMoreOpenCasesSequence'; import { showMoreResultsSequence } from './sequences/showMoreResultsSequence'; import { signOutSequence } from './sequences/signOutSequence'; import { startScanSequence } from './sequences/startScanSequence'; import { state } from './state'; import { submitAddConsolidatedCaseSequence } from './sequences/submitAddConsolidatedCaseSequence'; +import { submitAddDeficiencyStatisticsSequence } from './sequences/submitAddDeficiencyStatisticsSequence'; +import { submitAddOtherStatisticsSequence } from './sequences/submitAddOtherStatisticsSequence'; import { submitAddPractitionerSequence } from './sequences/submitAddPractitionerSequence'; import { submitCaseAdvancedSearchSequence } from './sequences/submitCaseAdvancedSearchSequence'; import { submitCaseAssociationRequestSequence } from './sequences/submitCaseAssociationRequestSequence'; @@ -274,9 +281,11 @@ import { submitCourtIssuedDocketEntrySequence } from './sequences/submitCourtIss import { submitCourtIssuedOrderSequence } from './sequences/submitCourtIssuedOrderSequence'; import { submitCreateOrderModalSequence } from './sequences/submitCreateOrderModalSequence'; import { submitDocketEntrySequence } from './sequences/submitDocketEntrySequence'; +import { submitEditDeficiencyStatisticSequence } from './sequences/submitEditDeficiencyStatisticSequence'; import { submitEditDocketEntryMetaSequence } from './sequences/submitEditDocketEntryMetaSequence'; import { submitEditIrsPractitionersModalSequence } from './sequences/submitEditIrsPractitionersModalSequence'; import { submitEditOrderTitleModalSequence } from './sequences/submitEditOrderTitleModalSequence'; +import { submitEditOtherStatisticsSequence } from './sequences/submitEditOtherStatisticsSequence'; import { submitEditPrimaryContactSequence } from './sequences/submitEditPrimaryContactSequence'; import { submitEditPrivatePractitionersModalSequence } from './sequences/submitEditPrivatePractitionersModalSequence'; import { submitEditSecondaryContactSequence } from './sequences/submitEditSecondaryContactSequence'; @@ -299,14 +308,13 @@ import { toggleCaseDifferenceSequence } from './sequences/toggleCaseDifferenceSe import { toggleMenuSequence } from './sequences/toggleMenuSequence'; import { toggleMobileDocketSortSequence } from './sequences/toggleMobileDocketSortSequence'; import { toggleMobileMenuSequence } from './sequences/toggleMobileMenuSequence'; -import { toggleReportsMenuSequence } from './sequences/toggleReportsMenuSequence'; import { toggleUsaBannerDetailsSequence } from './sequences/toggleUsaBannerDetailsSequence'; import { toggleWorkingCopySortSequence } from './sequences/toggleWorkingCopySortSequence'; import { unauthorizedErrorSequence } from './sequences/unauthorizedErrorSequence'; import { unblockCaseFromTrialSequence } from './sequences/unblockCaseFromTrialSequence'; import { unidentifiedUserErrorSequence } from './sequences/unidentifiedUserErrorSequence'; import { unprioritizeCaseSequence } from './sequences/unprioritizeCaseSequence'; -import { unsetWorkQueueIsInternalSequence } from './sequences/unsetWorkQueueIsInternalSequence'; +import { updateAddDeficiencyFormValueSequence } from './sequences/updateAddDeficiencyFormValueSequence'; import { updateAdvancedOpinionSearchFormValueSequence } from './sequences/updateAdvancedOpinionSearchFormValueSequence'; import { updateAdvancedOrderSearchFormValueSequence } from './sequences/updateAdvancedOrderSearchFormValueSequence'; import { updateAdvancedSearchFormValueSequence } from './sequences/updateAdvancedSearchFormValueSequence'; @@ -315,9 +323,10 @@ import { updateCaseAssociationFormValueSequence } from './sequences/updateCaseAs import { updateCaseDeadlineSequence } from './sequences/updateCaseDeadlineSequence'; import { updateCaseNoteSequence } from './sequences/updateCaseNoteSequence'; import { updateCasePartyTypeSequence } from './sequences/updateCasePartyTypeSequence'; -import { updateCaseWorkingCopyUserNoteSequence } from './sequences/updateCaseWorkingCopyUserNoteSequence'; import { updateCompleteFormValueSequence } from './sequences/updateCompleteFormValueSequence'; import { updateCourtIssuedDocketEntryFormValueSequence } from './sequences/updateCourtIssuedDocketEntryFormValueSequence'; +import { updateCreateCaseMessageAttachmentsSequence } from './sequences/updateCreateCaseMessageAttachmentsSequence'; +import { updateCreateCaseMessageValueInModalSequence } from './sequences/updateCreateCaseMessageValueInModalSequence'; import { updateCreateOrderModalFormValueSequence } from './sequences/updateCreateOrderModalFormValueSequence'; import { updateDocketEntryFormValueSequence } from './sequences/updateDocketEntryFormValueSequence'; import { updateDocketEntryMetaDocumentFormValueSequence } from './sequences/updateDocketEntryMetaDocumentFormValueSequence'; @@ -343,12 +352,14 @@ import { updateSessionMetadataSequence } from './sequences/updateSessionMetadata import { updateStartCaseFormValueSequence } from './sequences/updateStartCaseFormValueSequence'; import { updateStartCaseInternalPartyTypeSequence } from './sequences/updateStartCaseInternalPartyTypeSequence'; import { updateStateSequence } from './sequences/updateStateSequence'; +import { updateStatisticsFormValueSequence } from './sequences/updateStatisticsFormValueSequence'; import { updateTrialSessionFormDataSequence } from './sequences/updateTrialSessionFormDataSequence'; import { updateTrialSessionSequence } from './sequences/updateTrialSessionSequence'; import { updateUserCaseNoteOnWorkingCopySequence } from './sequences/updateUserCaseNoteOnWorkingCopySequence'; import { updateWorkingCopySessionNoteSequence } from './sequences/updateWorkingCopySessionNoteSequence'; import { uploadCorrespondenceDocumentSequence } from './sequences/uploadCorrespondenceDocumentSequence'; import { uploadCourtIssuedDocumentSequence } from './sequences/uploadCourtIssuedDocumentSequence'; +import { validateAddDeficiencyStatisticsSequence } from './sequences/validateAddDeficiencyStatisticsSequence'; import { validateAddIrsPractitionerSequence } from './sequences/caseAssociation/validateAddIrsPractitionerSequence'; import { validateAddPractitionerSequence } from './sequences/validateAddPractitionerSequence'; import { validateAddPrivatePractitionerSequence } from './sequences/caseAssociation/validateAddPrivatePractitionerSequence'; @@ -361,6 +372,7 @@ import { validateCaseDetailSequence } from './sequences/validateCaseDetailSequen import { validateCaseDocketNumberSearchFormSequence } from './sequences/validateCaseDocketNumberSearchFormSequence'; import { validateCaseInventoryReportModalSequence } from './sequences/validateCaseInventoryReportModalSequence'; import { validateCourtIssuedDocketEntrySequence } from './sequences/validateCourtIssuedDocketEntrySequence'; +import { validateCreateCaseMessageInModalSequence } from './sequences/validateCreateCaseMessageInModalSequence'; import { validateDocketEntrySequence } from './sequences/validateDocketEntrySequence'; import { validateDocketRecordSequence } from './sequences/validateDocketRecordSequence'; import { validateEditIrsPractitionersSequence } from './sequences/caseAssociation/validateEditIrsPractitionersSequence'; @@ -421,9 +433,11 @@ export const presenter = { batchDownloadReadySequence, batchDownloadTrialSessionSequence, blockCaseFromTrialSequence, + calculatePenaltiesForAddSequence, calculatePenaltiesSequence, cancelAddDraftDocumentSequence, - cancelEditPrimaryContactSequence, + cancelAddStatisticSequence, + cancelAndNavigateToCorrespondenceSequence, cancelFileUploadSequence, caseInventoryReportLoadMoreSequence, cerebralBindSimpleSetStateSequence, @@ -438,7 +452,6 @@ export const presenter = { clearModalSequence, clearPdfPreviewUrlSequence, clearPreferredTrialCitySequence, - clearWizardDataSequence, closeModalAndReturnToCaseDetailDraftDocumentsSequence, closeModalAndReturnToCaseDetailSequence, closeModalAndReturnToDashboardSequence, @@ -453,21 +466,23 @@ export const presenter = { contactSecondaryCountryTypeChangeSequence, convertHtml2PdfSequence, copyPrimaryContactSequence, - countryTypeFormContactChangeSequence, countryTypeUserContactChangeSequence, createCaseDeadlineSequence, + createCaseMessageSequence, createWorkItemSequence, deleteCaseDeadlineSequence, deleteCaseNoteSequence, + deleteCorrespondenceDocumentSequence, + deleteDeficiencyStatisticsSequence, deleteJudgesCaseNoteFromCaseDetailSequence, + deleteOtherStatisticsSequence, deleteTrialSessionSequence, deleteUserCaseNoteFromWorkingCopySequence, deleteWorkingCopySessionNoteSequence, dismissAlertSequence, dismissCreateMessageModalSequence, dismissModalSequence, - editSelectedDocumentSequence, - editSelectedSecondaryDocumentSequence, + editCorrespondenceDocumentSequence, editUploadCourtIssuedDocumentSequence, fetchPendingItemsSequence, fetchUserNotificationsSequence, @@ -476,11 +491,13 @@ export const presenter = { generatePdfFromScanSessionSequence, getBlockedCasesByTrialLocationSequence, getCaseInventoryReportSequence, + getCasesByStatusForUserSequence, getUsersInSectionSequence, - goBackToStartCaseInternalSequence, gotoAccessibilityStatementSequence, gotoAddCourtIssuedDocketEntrySequence, + gotoAddDeficiencyStatisticsSequence, gotoAddDocketEntrySequence, + gotoAddOtherStatisticsSequence, gotoAddTrialSessionSequence, gotoAdvancedSearchSequence, gotoAllCaseDeadlinesSequence, @@ -489,26 +506,30 @@ export const presenter = { gotoBlockedCasesReportSequence, gotoCaseDetailSequence, gotoCaseInventoryReportSequence, + gotoCaseMessagesSequence, gotoCaseSearchNoMatchesSequence, gotoCompleteDocketEntrySequence, gotoCreateOrderSequence, gotoCreatePractitionerUserSequence, gotoDashboardSequence, gotoDocumentDetailSequence, + gotoEditCorrespondenceDocumentSequence, gotoEditCourtIssuedDocketEntrySequence, + gotoEditDeficiencyStatisticSequence, gotoEditDocketEntryMetaSequence, gotoEditDocketEntrySequence, gotoEditOrderSequence, + gotoEditOtherStatisticsSequence, gotoEditPetitionDetailsSequence, gotoEditPetitionerInformationSequence, gotoEditPractitionerUserSequence, - gotoEditSavedPetitionSequence, gotoEditTrialSessionSequence, gotoEditUploadCourtIssuedDocumentSequence, gotoFileDocumentSequence, gotoFilePetitionSuccessSequence, gotoIdleLogoutSequence, gotoLoginSequence, + gotoMessageDetailSequence, gotoMessagesSequence, gotoPdfPreviewSequence, gotoPendingReportSequence, @@ -524,7 +545,6 @@ export const presenter = { gotoRequestAccessSequence, gotoReviewSavedPetitionSequence, gotoSecondaryContactEditSequence, - gotoSelectDocumentTypeSequence, gotoSignOrderSequence, gotoSignPDFDocumentSequence, gotoStartCaseWizardSequence, @@ -551,7 +571,6 @@ export const presenter = { navigateToPrintPaperServiceSequence, navigateToPrintableCaseConfirmationSequence, navigateToPrintableDocketRecordSequence, - navigateToReviewSavedPetitionSequence, notFoundErrorSequence, noticeGenerationCompleteSequence, openAddEditCaseNoteModalSequence, @@ -570,6 +589,8 @@ export const presenter = { openCompleteSelectDocumentTypeModalSequence, openConfirmDeleteBatchModalSequence, openConfirmDeleteCorrespondenceModalSequence, + openConfirmDeleteDeficiencyStatisticsModalSequence, + openConfirmDeleteOtherStatisticsModalSequence, openConfirmDeletePDFModalSequence, openConfirmDeleteTrialSessionModalSequence, openConfirmEditModalSequence, @@ -578,6 +599,7 @@ export const presenter = { openConfirmRescanBatchModalSequence, openConfirmServeToIrsModalSequence, openCreateCaseDeadlineModalSequence, + openCreateCaseMessageModalSequence, openCreateMessageAlongsideDocketRecordQCModalSequence, openCreateMessageModalSequence, openCreateOrderChooseTypeModalSequence, @@ -602,7 +624,6 @@ export const presenter = { printTrialCalendarSequence, prioritizeCaseSequence, redirectToLoginSequence, - refreshCaseSequence, refreshPdfSequence, refreshStatisticsSequence, removeBatchSequence, @@ -620,7 +641,6 @@ export const presenter = { runTrialSessionPlanningReportSequence, saveDocumentSigningSequence, saveSavedCaseForLaterSequence, - scannerShutdownSequence, scannerStartupSequence, sealCaseSequence, selectAssigneeSequence, @@ -628,29 +648,24 @@ export const presenter = { selectDocumentForPetitionQcPreviewSequence, selectDocumentForPreviewSequence, selectDocumentForScanSequence, - selectDocumentSequence, selectScannerSequence, - selectSecondaryDocumentSequence, selectWorkItemSequence, serveCaseToIrsSequence, serveCourtIssuedDocumentSequence, - setCanvasForPDFSigningSequence, setCaseDetailPageTabSequence, setCaseDetailPrimaryTabSequence, + setCaseTypeToDisplaySequence, setCurrentPageIndexSequence, - setDocumentDetailTabSequence, setDocumentForUploadSequence, setDocumentUploadModeSequence, setFieldOrderSequence, setFocusedWorkItemSequence, setIdleStatusIdleSequence, setIrsNoticeFalseSequence, - setModalDialogNameSequence, setPDFPageForSigningSequence, setPDFSignatureDataSequence, setPageSequence, setPdfPreviewUrlSequence, - setScannerSourceSequence, setSelectedBatchIndexSequence, setSelectedCasesForConsolidatedCaseDocumentSubmissionSequence, setTrialSessionCalendarSequence, @@ -658,10 +673,14 @@ export const presenter = { setWorkQueueIsInternalSequence, showCalculatePenaltiesModalSequence, showDocketRecordDetailModalSequence, + showMoreClosedCasesSequence, + showMoreOpenCasesSequence, showMoreResultsSequence, signOutSequence, startScanSequence, submitAddConsolidatedCaseSequence, + submitAddDeficiencyStatisticsSequence, + submitAddOtherStatisticsSequence, submitAddPractitionerSequence, submitCaseAdvancedSearchSequence, submitCaseAssociationRequestSequence, @@ -674,9 +693,11 @@ export const presenter = { submitCourtIssuedOrderSequence, submitCreateOrderModalSequence, submitDocketEntrySequence, + submitEditDeficiencyStatisticSequence, submitEditDocketEntryMetaSequence, submitEditIrsPractitionersModalSequence, submitEditOrderTitleModalSequence, + submitEditOtherStatisticsSequence, submitEditPrimaryContactSequence, submitEditPrivatePractitionersModalSequence, submitEditSecondaryContactSequence, @@ -699,14 +720,13 @@ export const presenter = { toggleMenuSequence, toggleMobileDocketSortSequence, toggleMobileMenuSequence, - toggleReportsMenuSequence, toggleUsaBannerDetailsSequence, toggleWorkingCopySortSequence, unauthorizedErrorSequence, unblockCaseFromTrialSequence, unidentifiedUserErrorSequence, unprioritizeCaseSequence, - unsetWorkQueueIsInternalSequence, + updateAddDeficiencyFormValueSequence, updateAdvancedOpinionSearchFormValueSequence, updateAdvancedOrderSearchFormValueSequence, updateAdvancedSearchFormValueSequence, @@ -715,9 +735,10 @@ export const presenter = { updateCaseDeadlineSequence, updateCaseNoteSequence, updateCasePartyTypeSequence, - updateCaseWorkingCopyUserNoteSequence, updateCompleteFormValueSequence, updateCourtIssuedDocketEntryFormValueSequence, + updateCreateCaseMessageAttachmentsSequence, + updateCreateCaseMessageValueInModalSequence, updateCreateOrderModalFormValueSequence, updateDocketEntryFormValueSequence, updateDocketEntryMetaDocumentFormValueSequence, @@ -743,12 +764,14 @@ export const presenter = { updateStartCaseFormValueSequence, updateStartCaseInternalPartyTypeSequence, updateStateSequence, + updateStatisticsFormValueSequence, updateTrialSessionFormDataSequence, updateTrialSessionSequence, updateUserCaseNoteOnWorkingCopySequence, updateWorkingCopySessionNoteSequence, uploadCorrespondenceDocumentSequence, uploadCourtIssuedDocumentSequence, + validateAddDeficiencyStatisticsSequence, validateAddIrsPractitionerSequence, validateAddPractitionerSequence, validateAddPrivatePractitionerSequence, @@ -761,6 +784,7 @@ export const presenter = { validateCaseDocketNumberSearchFormSequence, validateCaseInventoryReportModalSequence, validateCourtIssuedDocketEntrySequence, + validateCreateCaseMessageInModalSequence, validateDocketEntrySequence, validateDocketRecordSequence, validateEditIrsPractitionersSequence, diff --git a/web-client/src/presenter/sequences/calculatePenaltiesForAddSequence.js b/web-client/src/presenter/sequences/calculatePenaltiesForAddSequence.js new file mode 100644 index 00000000000..fea27f763b7 --- /dev/null +++ b/web-client/src/presenter/sequences/calculatePenaltiesForAddSequence.js @@ -0,0 +1,9 @@ +import { calculatePenaltiesAction } from '../actions/calculatePenaltiesAction'; +import { clearModalAction } from '../actions/clearModalAction'; +import { setTotalPenaltiesAmountForAddStatisticAction } from '../actions/setTotalPenaltiesAmountForAddStatisticAction'; + +export const calculatePenaltiesForAddSequence = [ + calculatePenaltiesAction, + setTotalPenaltiesAmountForAddStatisticAction, + clearModalAction, +]; diff --git a/web-client/src/presenter/sequences/cancelAddStatisticSequence.js b/web-client/src/presenter/sequences/cancelAddStatisticSequence.js new file mode 100644 index 00000000000..aaf59df5820 --- /dev/null +++ b/web-client/src/presenter/sequences/cancelAddStatisticSequence.js @@ -0,0 +1,7 @@ +import { navigateToCaseDetailCaseInformationAction } from '../actions/navigateToCaseDetailCaseInformationAction'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; + +export const cancelAddStatisticSequence = [ + setCaseDetailPageTabFrozenAction, + navigateToCaseDetailCaseInformationAction, +]; diff --git a/web-client/src/presenter/sequences/cancelAndNavigateToCorrespondenceSequence.js b/web-client/src/presenter/sequences/cancelAndNavigateToCorrespondenceSequence.js new file mode 100644 index 00000000000..be582f96fbc --- /dev/null +++ b/web-client/src/presenter/sequences/cancelAndNavigateToCorrespondenceSequence.js @@ -0,0 +1,11 @@ +import { clearModalAction } from '../actions/clearModalAction'; +import { navigateToCaseDetailAction } from '../actions/navigateToCaseDetailAction'; +import { setCaseDetailPageTabActionGenerator } from '../actions/setCaseDetailPageTabActionGenerator'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; + +export const cancelAndNavigateToCorrespondenceSequence = [ + clearModalAction, + setCaseDetailPageTabActionGenerator('correspondence'), + setCaseDetailPageTabFrozenAction, + navigateToCaseDetailAction, +]; diff --git a/web-client/src/presenter/sequences/cancelEditPrimaryContactSequence.js b/web-client/src/presenter/sequences/cancelEditPrimaryContactSequence.js deleted file mode 100644 index cf2d38cd5a4..00000000000 --- a/web-client/src/presenter/sequences/cancelEditPrimaryContactSequence.js +++ /dev/null @@ -1,7 +0,0 @@ -import { clearErrorAlertsAction } from '../actions/clearErrorAlertsAction'; -import { navigateToCaseDetailAction } from '../actions/navigateToCaseDetailAction'; - -export const cancelEditPrimaryContactSequence = [ - clearErrorAlertsAction, - navigateToCaseDetailAction, -]; diff --git a/web-client/src/presenter/sequences/clearWizardDataSequence.js b/web-client/src/presenter/sequences/clearWizardDataSequence.js deleted file mode 100644 index 3cf9ded835d..00000000000 --- a/web-client/src/presenter/sequences/clearWizardDataSequence.js +++ /dev/null @@ -1,3 +0,0 @@ -import { clearWizardDataAction } from '../actions/FileDocument/clearWizardDataAction'; - -export const clearWizardDataSequence = [clearWizardDataAction]; diff --git a/web-client/src/presenter/sequences/closeModalAndReturnToCaseDetailDraftDocumentsSequence.js b/web-client/src/presenter/sequences/closeModalAndReturnToCaseDetailDraftDocumentsSequence.js index f2c190606da..e12245f4db2 100644 --- a/web-client/src/presenter/sequences/closeModalAndReturnToCaseDetailDraftDocumentsSequence.js +++ b/web-client/src/presenter/sequences/closeModalAndReturnToCaseDetailDraftDocumentsSequence.js @@ -1,16 +1,14 @@ import { clearModalAction } from '../actions/clearModalAction'; import { navigateToCaseDetailAction } from '../actions/navigateToCaseDetailAction'; -import { setCaseDetailPageTabAction } from '../actions/setCaseDetailPageTabAction'; +import { setCaseDetailPageTabActionGenerator } from '../actions/setCaseDetailPageTabActionGenerator'; import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; import { setCasePropFromStateAction } from '../actions/setCasePropFromStateAction'; import { setIsPrimaryTabAction } from '../actions/setIsPrimaryTabAction'; -import { setTabToInProgressAction } from '../actions/editUploadCourtIssuedDocument/setTabToInProgressAction'; export const closeModalAndReturnToCaseDetailDraftDocumentsSequence = [ clearModalAction, setCasePropFromStateAction, - setTabToInProgressAction, - setCaseDetailPageTabAction, + setCaseDetailPageTabActionGenerator('inProgress'), setIsPrimaryTabAction, setCaseDetailPageTabFrozenAction, navigateToCaseDetailAction, diff --git a/web-client/src/presenter/sequences/completeDocketEntryQCSequence.js b/web-client/src/presenter/sequences/completeDocketEntryQCSequence.js index d247782d53d..4bcb7861896 100644 --- a/web-client/src/presenter/sequences/completeDocketEntryQCSequence.js +++ b/web-client/src/presenter/sequences/completeDocketEntryQCSequence.js @@ -5,7 +5,7 @@ import { computeDateReceivedAction } from '../actions/DocketEntry/computeDateRec import { computeFormDateAction } from '../actions/computeFormDateAction'; import { computeSecondaryFormDateAction } from '../actions/FileDocument/computeSecondaryFormDateAction'; import { generateTitleAction } from '../actions/FileDocument/generateTitleAction'; -import { navigateToCaseDetailAction } from '../actions/navigateToCaseDetailAction'; +import { navigateToDocumentQCAction } from '../actions/navigateToDocumentQCAction'; import { setAlertErrorAction } from '../actions/setAlertErrorAction'; import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; import { setCaseAction } from '../actions/setCaseAction'; @@ -42,7 +42,7 @@ export const completeDocketEntryQCSequence = [ setAlertSuccessAction, setPaperServicePartiesAction, setSaveAlertsForNavigationAction, - navigateToCaseDetailAction, + navigateToDocumentQCAction, clearErrorAlertsAction, ], }, diff --git a/web-client/src/presenter/sequences/countryTypeFormContactChangeSequence.js b/web-client/src/presenter/sequences/countryTypeFormContactChangeSequence.js deleted file mode 100644 index 2afa243e6e1..00000000000 --- a/web-client/src/presenter/sequences/countryTypeFormContactChangeSequence.js +++ /dev/null @@ -1,11 +0,0 @@ -import { clearAlertsAction } from '../actions/clearAlertsAction'; -import { countryTypeFormContactChangeAction } from '../actions/countryTypeFormContactChangeAction'; -import { setFormValueAction } from '../actions/setFormValueAction'; -import { stopShowValidationAction } from '../actions/stopShowValidationAction'; - -export const countryTypeFormContactChangeSequence = [ - countryTypeFormContactChangeAction, - stopShowValidationAction, - setFormValueAction, - clearAlertsAction, -]; diff --git a/web-client/src/presenter/sequences/updateCaseWorkingCopyUserNoteSequence.js b/web-client/src/presenter/sequences/createCaseMessageSequence.js similarity index 51% rename from web-client/src/presenter/sequences/updateCaseWorkingCopyUserNoteSequence.js rename to web-client/src/presenter/sequences/createCaseMessageSequence.js index c4739631df4..132b79ffefb 100644 --- a/web-client/src/presenter/sequences/updateCaseWorkingCopyUserNoteSequence.js +++ b/web-client/src/presenter/sequences/createCaseMessageSequence.js @@ -1,26 +1,28 @@ import { clearAlertsAction } from '../actions/clearAlertsAction'; import { clearModalAction } from '../actions/clearModalAction'; import { clearModalStateAction } from '../actions/clearModalStateAction'; +import { clearScreenMetadataAction } from '../actions/clearScreenMetadataAction'; +import { clearUsersAction } from '../actions/clearUsersAction'; +import { createCaseMessageAction } from '../actions/CaseDetail/createCaseMessageAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; import { startShowValidationAction } from '../actions/startShowValidationAction'; import { stopShowValidationAction } from '../actions/stopShowValidationAction'; -import { updateNotePropsFromModalStateAction } from '../actions/TrialSessionWorkingCopy/updateNotePropsFromModalStateAction'; -import { updateTrialSessionWorkingCopyAction } from '../actions/TrialSession/updateTrialSessionWorkingCopyAction'; -import { updateUserCaseNoteInTrialSessionWorkingCopyAction } from '../actions/TrialSessionWorkingCopy/updateUserCaseNoteInTrialSessionWorkingCopyAction'; -import { validateNoteAction } from '../actions/validateNoteAction'; +import { validateCreateCaseMessageAction } from '../actions/validateCreateCaseMessageAction'; -export const updateCaseWorkingCopyUserNoteSequence = [ +export const createCaseMessageSequence = [ + clearAlertsAction, startShowValidationAction, - validateNoteAction, + validateCreateCaseMessageAction, { error: [setValidationErrorsAction], success: showProgressSequenceDecorator([ + createCaseMessageAction, stopShowValidationAction, - clearAlertsAction, - updateNotePropsFromModalStateAction, - updateUserCaseNoteInTrialSessionWorkingCopyAction, - updateTrialSessionWorkingCopyAction, + setAlertSuccessAction, + clearScreenMetadataAction, + clearUsersAction, clearModalAction, clearModalStateAction, ]), diff --git a/web-client/src/presenter/sequences/deleteCorrespondenceDocumentSequence.js b/web-client/src/presenter/sequences/deleteCorrespondenceDocumentSequence.js new file mode 100644 index 00000000000..7459d7d1e85 --- /dev/null +++ b/web-client/src/presenter/sequences/deleteCorrespondenceDocumentSequence.js @@ -0,0 +1,27 @@ +import { clearModalAction } from '../actions/clearModalAction'; +import { clearModalStateAction } from '../actions/clearModalStateAction'; +import { deleteCorrespondenceDocumentAction } from '../actions/CorrespondenceDocument/deleteCorrespondenceDocumentAction'; +import { getCaseAction } from '../actions/getCaseAction'; +import { getDeleteCorrespondenceDocumentAlertErrorAction } from '../actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertErrorAction'; +import { getDeleteCorrespondenceDocumentAlertSuccessAction } from '../actions/CorrespondenceDocument/getDeleteCorrespondenceDocumentAlertSuccessAction'; +import { setAlertErrorAction } from '../actions/setAlertErrorAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseAction } from '../actions/setCaseAction'; + +export const deleteCorrespondenceDocumentSequence = [ + deleteCorrespondenceDocumentAction, + { + error: [ + getDeleteCorrespondenceDocumentAlertErrorAction, + setAlertErrorAction, + ], + success: [ + getDeleteCorrespondenceDocumentAlertSuccessAction, + setAlertSuccessAction, + getCaseAction, + setCaseAction, + ], + }, + clearModalAction, + clearModalStateAction, +]; diff --git a/web-client/src/presenter/sequences/deleteDeficiencyStatisticsSequence.js b/web-client/src/presenter/sequences/deleteDeficiencyStatisticsSequence.js new file mode 100644 index 00000000000..de00b5e1674 --- /dev/null +++ b/web-client/src/presenter/sequences/deleteDeficiencyStatisticsSequence.js @@ -0,0 +1,25 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { clearModalAction } from '../actions/clearModalAction'; +import { deleteDeficiencyStatisticsAction } from '../actions/deleteDeficiencyStatisticsAction'; +import { navigateToCaseDetailCaseInformationAction } from '../actions/navigateToCaseDetailCaseInformationAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; +import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; + +export const deleteDeficiencyStatisticsSequence = [ + showProgressSequenceDecorator([ + deleteDeficiencyStatisticsAction, + { + error: [], + success: [ + clearFormAction, + setSaveAlertsForNavigationAction, + setCaseDetailPageTabFrozenAction, + setAlertSuccessAction, + clearModalAction, + navigateToCaseDetailCaseInformationAction, + ], + }, + ]), +]; diff --git a/web-client/src/presenter/sequences/deleteOtherStatisticsSequence.js b/web-client/src/presenter/sequences/deleteOtherStatisticsSequence.js new file mode 100644 index 00000000000..44cca4f2556 --- /dev/null +++ b/web-client/src/presenter/sequences/deleteOtherStatisticsSequence.js @@ -0,0 +1,25 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { clearModalAction } from '../actions/clearModalAction'; +import { deleteOtherStatisticsAction } from '../actions/deleteOtherStatisticsAction'; +import { navigateToCaseDetailCaseInformationAction } from '../actions/navigateToCaseDetailCaseInformationAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; +import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; + +export const deleteOtherStatisticsSequence = [ + showProgressSequenceDecorator([ + deleteOtherStatisticsAction, + { + error: [], + success: [ + clearFormAction, + setSaveAlertsForNavigationAction, + setCaseDetailPageTabFrozenAction, + setAlertSuccessAction, + clearModalAction, + navigateToCaseDetailCaseInformationAction, + ], + }, + ]), +]; diff --git a/web-client/src/presenter/sequences/editCorrespondenceDocumentSequence.js b/web-client/src/presenter/sequences/editCorrespondenceDocumentSequence.js new file mode 100644 index 00000000000..c7aa19e16a1 --- /dev/null +++ b/web-client/src/presenter/sequences/editCorrespondenceDocumentSequence.js @@ -0,0 +1,63 @@ +import { chooseByTruthyStateActionFactory } from '../actions/editUploadCourtIssuedDocument/chooseByTruthyStateActionFactory'; +import { clearAlertsAction } from '../actions/clearAlertsAction'; +import { getEditCorrespondenceDocumentAlertSuccessAction } from '../actions/CorrespondenceDocument/getEditCorrespondenceDocumentAlertSuccessAction'; +import { navigateToCaseDetailAction } from '../actions/navigateToCaseDetailAction'; +import { openFileUploadErrorModal } from '../actions/openFileUploadErrorModal'; +import { overwriteCorrespondenceFileAction } from '../actions/CourtIssuedOrder/overwriteCorrespondenceFileAction'; +import { setAlertErrorAction } from '../actions/setAlertErrorAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseAction } from '../actions/setCaseAction'; +import { setCaseDetailPageTabActionGenerator } from '../actions/setCaseDetailPageTabActionGenerator'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; +import { setDocumentTitleFromFormAction } from '../actions/CorrespondenceDocument/setDocumentTitleFromFormAction'; +import { setPrimaryDocumentFileIdPropAction } from '../actions/editUploadCourtIssuedDocument/setPrimaryDocumentFileIdPropAction'; +import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; +import { setValidationAlertErrorsAction } from '../actions/setValidationAlertErrorsAction'; +import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; +import { startShowValidationAction } from '../actions/startShowValidationAction'; +import { stopShowValidationAction } from '../actions/stopShowValidationAction'; +import { submitCorrespondenceAction } from '../actions/CorrespondenceDocument/submitCorrespondenceAction'; +import { unsetDocumentToEditAction } from '../actions/editUploadCourtIssuedDocument/unsetDocumentToEditAction'; +import { validateUploadCorrespondenceDocumentAction } from '../actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction'; + +const onError = [openFileUploadErrorModal]; +const onSuccess = [ + setDocumentTitleFromFormAction, + submitCorrespondenceAction, + setCaseAction, + getEditCorrespondenceDocumentAlertSuccessAction, + setAlertSuccessAction, + setSaveAlertsForNavigationAction, + setCaseDetailPageTabActionGenerator(), + setCaseDetailPageTabFrozenAction, + navigateToCaseDetailAction, +]; + +export const editCorrespondenceDocumentSequence = [ + startShowValidationAction, + validateUploadCorrespondenceDocumentAction, + { + error: [ + setAlertErrorAction, + setValidationErrorsAction, + setValidationAlertErrorsAction, + ], + success: showProgressSequenceDecorator([ + stopShowValidationAction, + clearAlertsAction, + chooseByTruthyStateActionFactory('screenMetadata.documentReset'), + { + no: [setPrimaryDocumentFileIdPropAction, onSuccess], + yes: [ + overwriteCorrespondenceFileAction, + { + error: onError, + success: onSuccess, + }, + ], + }, + unsetDocumentToEditAction, + ]), + }, +]; diff --git a/web-client/src/presenter/sequences/editSelectedDocumentSequence.js b/web-client/src/presenter/sequences/editSelectedDocumentSequence.js deleted file mode 100644 index 914d99131f3..00000000000 --- a/web-client/src/presenter/sequences/editSelectedDocumentSequence.js +++ /dev/null @@ -1,10 +0,0 @@ -import { clearDocumentScenarioAction } from '../actions/FileDocument/clearDocumentScenarioAction'; -import { clearSecondaryDocumentFormAction } from '../actions/FileDocument/clearSecondaryDocumentFormAction'; -import { set } from 'cerebral/factories'; -import { state } from 'cerebral'; - -export const editSelectedDocumentSequence = [ - set(state.screenMetadata.isDocumentTypeSelected, false), - clearDocumentScenarioAction, - clearSecondaryDocumentFormAction, -]; diff --git a/web-client/src/presenter/sequences/editSelectedSecondaryDocumentSequence.js b/web-client/src/presenter/sequences/editSelectedSecondaryDocumentSequence.js deleted file mode 100644 index a3bd3f0e581..00000000000 --- a/web-client/src/presenter/sequences/editSelectedSecondaryDocumentSequence.js +++ /dev/null @@ -1,8 +0,0 @@ -import { clearSecondaryDocumentScenarioAction } from '../actions/FileDocument/clearSecondaryDocumentScenarioAction'; -import { set } from 'cerebral/factories'; -import { state } from 'cerebral'; - -export const editSelectedSecondaryDocumentSequence = [ - set(state.screenMetadata.isSecondaryDocumentTypeSelected, false), - clearSecondaryDocumentScenarioAction, -]; diff --git a/web-client/src/presenter/sequences/editUploadCourtIssuedDocumentSequence.js b/web-client/src/presenter/sequences/editUploadCourtIssuedDocumentSequence.js index 3af5c7796ff..bec22fb22ae 100644 --- a/web-client/src/presenter/sequences/editUploadCourtIssuedDocumentSequence.js +++ b/web-client/src/presenter/sequences/editUploadCourtIssuedDocumentSequence.js @@ -8,7 +8,7 @@ import { overwriteOrderFileAction } from '../actions/CourtIssuedOrder/overwriteO import { setAlertErrorAction } from '../actions/setAlertErrorAction'; import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; import { setCaseAction } from '../actions/setCaseAction'; -import { setCaseDetailPageTabAction } from '../actions/setCaseDetailPageTabAction'; +import { setCaseDetailPageTabActionGenerator } from '../actions/setCaseDetailPageTabActionGenerator'; import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; import { setIsPrimaryTabAction } from '../actions/setIsPrimaryTabAction'; import { setPrimaryDocumentFileIdPropAction } from '../actions/editUploadCourtIssuedDocument/setPrimaryDocumentFileIdPropAction'; @@ -32,7 +32,7 @@ const onSuccess = [ getUploadCourtIssuedDocumentAlertSuccessAction, setAlertSuccessAction, setSaveAlertsForNavigationAction, - setCaseDetailPageTabAction, + setCaseDetailPageTabActionGenerator(), setIsPrimaryTabAction, setCaseDetailPageTabFrozenAction, navigateToCaseDetailAction, diff --git a/web-client/src/presenter/sequences/getCasesByStatusForUserSequence.js b/web-client/src/presenter/sequences/getCasesByStatusForUserSequence.js new file mode 100644 index 00000000000..a77a2ac0b5a --- /dev/null +++ b/web-client/src/presenter/sequences/getCasesByStatusForUserSequence.js @@ -0,0 +1,8 @@ +import { getOpenAndClosedCasesByUserAction } from '../actions/caseConsolidation/getOpenAndClosedCasesByUserAction'; +import { setCasesAction } from '../actions/setCasesAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; + +export const getCasesByStatusForUserSequence = showProgressSequenceDecorator([ + getOpenAndClosedCasesByUserAction, + setCasesAction, +]); diff --git a/web-client/src/presenter/sequences/goBackToStartCaseInternalSequence.js b/web-client/src/presenter/sequences/goBackToStartCaseInternalSequence.js deleted file mode 100644 index cf7f2992ad2..00000000000 --- a/web-client/src/presenter/sequences/goBackToStartCaseInternalSequence.js +++ /dev/null @@ -1,13 +0,0 @@ -import { getInitialNextStepAction } from '../actions/StartCaseInternal/getInitialNextStepAction'; -import { navigateToStartCaseWizardNextStepAction } from '../actions/StartCase/navigateToStartCaseWizardNextStepAction'; -import { selectDocumentForScanSequence } from './selectDocumentForScanSequence'; -import { setCurrentPageAction } from '../actions/setCurrentPageAction'; -import { setStartInternalCaseTabAction } from '../actions/StartCaseInternal/setStartInternalCaseTabAction'; - -export const goBackToStartCaseInternalSequence = [ - setCurrentPageAction('StartCaseInternal'), - getInitialNextStepAction, - setStartInternalCaseTabAction, - navigateToStartCaseWizardNextStepAction, - selectDocumentForScanSequence, -]; diff --git a/web-client/src/presenter/sequences/gotoAddDeficiencyStatisticsSequence.js b/web-client/src/presenter/sequences/gotoAddDeficiencyStatisticsSequence.js new file mode 100644 index 00000000000..e4efef1f854 --- /dev/null +++ b/web-client/src/presenter/sequences/gotoAddDeficiencyStatisticsSequence.js @@ -0,0 +1,24 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { getCaseAction } from '../actions/getCaseAction'; +import { isLoggedInAction } from '../actions/isLoggedInAction'; +import { redirectToCognitoAction } from '../actions/redirectToCognitoAction'; +import { setCaseAction } from '../actions/setCaseAction'; +import { setCurrentPageAction } from '../actions/setCurrentPageAction'; +import { setDefaultFormForAddDeficiencySatisticsAction } from '../actions/setDefaultFormForAddDeficiencySatisticsAction'; +import { stopShowValidationAction } from '../actions/stopShowValidationAction'; + +export const gotoAddDeficiencyStatisticsSequence = [ + isLoggedInAction, + { + isLoggedIn: [ + setCurrentPageAction('Interstitial'), + stopShowValidationAction, + clearFormAction, + getCaseAction, + setCaseAction, + setDefaultFormForAddDeficiencySatisticsAction, + setCurrentPageAction('AddDeficiencyStatistics'), + ], + unauthorized: [redirectToCognitoAction], + }, +]; diff --git a/web-client/src/presenter/sequences/gotoSelectDocumentTypeSequence.js b/web-client/src/presenter/sequences/gotoAddOtherStatisticsSequence.js similarity index 50% rename from web-client/src/presenter/sequences/gotoSelectDocumentTypeSequence.js rename to web-client/src/presenter/sequences/gotoAddOtherStatisticsSequence.js index d8608910cef..cbaa0bbfeb1 100644 --- a/web-client/src/presenter/sequences/gotoSelectDocumentTypeSequence.js +++ b/web-client/src/presenter/sequences/gotoAddOtherStatisticsSequence.js @@ -1,20 +1,22 @@ +import { clearFormAction } from '../actions/clearFormAction'; import { getCaseAction } from '../actions/getCaseAction'; import { isLoggedInAction } from '../actions/isLoggedInAction'; import { redirectToCognitoAction } from '../actions/redirectToCognitoAction'; import { setCaseAction } from '../actions/setCaseAction'; import { setCurrentPageAction } from '../actions/setCurrentPageAction'; +import { stopShowValidationAction } from '../actions/stopShowValidationAction'; -const gotoSelectDocumentType = [ - setCurrentPageAction('Interstitial'), - getCaseAction, - setCaseAction, - setCurrentPageAction('SelectDocumentType'), -]; - -export const gotoSelectDocumentTypeSequence = [ +export const gotoAddOtherStatisticsSequence = [ isLoggedInAction, { - isLoggedIn: gotoSelectDocumentType, + isLoggedIn: [ + setCurrentPageAction('Interstitial'), + stopShowValidationAction, + clearFormAction, + getCaseAction, + setCaseAction, + setCurrentPageAction('AddOtherStatistics'), + ], unauthorized: [redirectToCognitoAction], }, ]; diff --git a/web-client/src/presenter/sequences/gotoCaseDetailSequence.js b/web-client/src/presenter/sequences/gotoCaseDetailSequence.js index 54a3bed9973..8b167756d27 100644 --- a/web-client/src/presenter/sequences/gotoCaseDetailSequence.js +++ b/web-client/src/presenter/sequences/gotoCaseDetailSequence.js @@ -10,7 +10,6 @@ import { getConstants } from '../../getConstants'; import { getJudgesCaseNoteForCaseAction } from '../actions/TrialSession/getJudgesCaseNoteForCaseAction'; import { parallel, set } from 'cerebral/factories'; import { runPathForUserRoleAction } from '../actions/runPathForUserRoleAction'; - import { setCaseAction } from '../actions/setCaseAction'; import { setCaseAssociationAction } from '../actions/setCaseAssociationAction'; import { setCaseDetailPageTabUnfrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabUnfrozenAction'; diff --git a/web-client/src/presenter/sequences/gotoCaseMessagesSequence.js b/web-client/src/presenter/sequences/gotoCaseMessagesSequence.js new file mode 100644 index 00000000000..6490d81fa2f --- /dev/null +++ b/web-client/src/presenter/sequences/gotoCaseMessagesSequence.js @@ -0,0 +1,34 @@ +import { chooseMessageBoxAction } from '../actions/chooseMessageBoxAction'; +import { clearErrorAlertsAction } from '../actions/clearErrorAlertsAction'; +import { closeMobileMenuAction } from '../actions/closeMobileMenuAction'; +import { getInboxCaseMessagesForSectionAction } from '../actions/getInboxCaseMessagesForSectionAction'; +import { getInboxCaseMessagesForUserAction } from '../actions/getInboxCaseMessagesForUserAction'; +import { getOutboxCaseMessagesForSectionAction } from '../actions/getOutboxCaseMessagesForSectionAction'; +import { getOutboxCaseMessagesForUserAction } from '../actions/getOutboxCaseMessagesForUserAction'; +import { isLoggedInAction } from '../actions/isLoggedInAction'; +import { redirectToCognitoAction } from '../actions/redirectToCognitoAction'; +import { setCaseMessagesAction } from '../actions/setCaseMessagesAction'; +import { setCurrentPageAction } from '../actions/setCurrentPageAction'; + +const goToCaseMessages = [ + setCurrentPageAction('Interstitial'), + closeMobileMenuAction, + clearErrorAlertsAction, + chooseMessageBoxAction, + { + myinbox: [getInboxCaseMessagesForUserAction], + myoutbox: [getOutboxCaseMessagesForUserAction], + sectioninbox: [getInboxCaseMessagesForSectionAction], + sectionoutbox: [getOutboxCaseMessagesForSectionAction], + }, + setCaseMessagesAction, + setCurrentPageAction('CaseMessages'), +]; + +export const gotoCaseMessagesSequence = [ + isLoggedInAction, + { + isLoggedIn: goToCaseMessages, + unauthorized: [redirectToCognitoAction], + }, +]; diff --git a/web-client/src/presenter/sequences/gotoDashboardSequence.js b/web-client/src/presenter/sequences/gotoDashboardSequence.js index 23e32b3cfa7..989aa22db4b 100644 --- a/web-client/src/presenter/sequences/gotoDashboardSequence.js +++ b/web-client/src/presenter/sequences/gotoDashboardSequence.js @@ -1,9 +1,9 @@ import { chooseWorkQueueSequence } from './chooseWorkQueueSequence'; import { clearErrorAlertsAction } from '../actions/clearErrorAlertsAction'; import { closeMobileMenuAction } from '../actions/closeMobileMenuAction'; -import { getConsolidatedCasesByUserAction } from '../actions/caseConsolidation/getConsolidatedCasesByUserAction'; import { getConstants } from '../../getConstants'; import { getJudgeForCurrentUserAction } from '../actions/getJudgeForCurrentUserAction'; +import { getOpenAndClosedCasesByUserAction } from '../actions/caseConsolidation/getOpenAndClosedCasesByUserAction'; import { getTrialSessionsAction } from '../actions/TrialSession/getTrialSessionsAction'; import { getUserAction } from '../actions/getUserAction'; import { isLoggedInAction } from '../actions/isLoggedInAction'; @@ -13,6 +13,7 @@ import { runPathForUserRoleAction } from '../actions/runPathForUserRoleAction'; import { set } from 'cerebral/factories'; import { setCasesAction } from '../actions/setCasesAction'; import { setCurrentPageAction } from '../actions/setCurrentPageAction'; +import { setDefaultCaseTypeToDisplayAction } from '../actions/setDefaultCaseTypeToDisplayAction'; import { setJudgeUserAction } from '../actions/setJudgeUserAction'; import { setMessageInboxPropsAction } from '../actions/setMessageInboxPropsAction'; import { setTrialSessionsAction } from '../actions/TrialSession/setTrialSessionsAction'; @@ -56,7 +57,8 @@ const goToDashboard = [ ], inactivePractitioner: [setCurrentPageAction('DashboardInactive')], irsPractitioner: [ - getConsolidatedCasesByUserAction, + setDefaultCaseTypeToDisplayAction, + getOpenAndClosedCasesByUserAction, setCasesAction, setCurrentPageAction('DashboardRespondent'), ], @@ -69,12 +71,14 @@ const goToDashboard = [ setCurrentPageAction('DashboardJudge'), ], petitioner: [ - getConsolidatedCasesByUserAction, + setDefaultCaseTypeToDisplayAction, + getOpenAndClosedCasesByUserAction, setCasesAction, setCurrentPageAction('DashboardPetitioner'), ], privatePractitioner: [ - getConsolidatedCasesByUserAction, + setDefaultCaseTypeToDisplayAction, + getOpenAndClosedCasesByUserAction, setCasesAction, setCurrentPageAction('DashboardPractitioner'), ], diff --git a/web-client/src/presenter/sequences/gotoEditCorrespondenceDocumentSequence.js b/web-client/src/presenter/sequences/gotoEditCorrespondenceDocumentSequence.js new file mode 100644 index 00000000000..cf23e6b1d61 --- /dev/null +++ b/web-client/src/presenter/sequences/gotoEditCorrespondenceDocumentSequence.js @@ -0,0 +1,30 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { clearScreenMetadataAction } from '../actions/clearScreenMetadataAction'; +import { getCaseAction } from '../actions/getCaseAction'; +import { isLoggedInAction } from '../actions/isLoggedInAction'; +import { redirectToCognitoAction } from '../actions/redirectToCognitoAction'; +import { setCaseAction } from '../actions/setCaseAction'; +import { setCurrentPageAction } from '../actions/setCurrentPageAction'; +import { setDocumentIdAction } from '../actions/setDocumentIdAction'; +import { setDocumentToFormAction } from '../actions/editUploadCourtIssuedDocument/setDocumentToFormAction'; +import { stopShowValidationAction } from '../actions/stopShowValidationAction'; + +const gotoEditCorrespondenceDocument = [ + setCurrentPageAction('Interstitial'), + stopShowValidationAction, + clearFormAction, + clearScreenMetadataAction, + getCaseAction, + setCaseAction, + setDocumentIdAction, + setDocumentToFormAction, + setCurrentPageAction('EditCorrespondenceDocument'), +]; + +export const gotoEditCorrespondenceDocumentSequence = [ + isLoggedInAction, + { + isLoggedIn: [gotoEditCorrespondenceDocument], + unauthorized: [redirectToCognitoAction], + }, +]; diff --git a/web-client/src/presenter/sequences/gotoEditDeficiencyStatisticSequence.js b/web-client/src/presenter/sequences/gotoEditDeficiencyStatisticSequence.js new file mode 100644 index 00000000000..eb74d5537bc --- /dev/null +++ b/web-client/src/presenter/sequences/gotoEditDeficiencyStatisticSequence.js @@ -0,0 +1,24 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { getCaseAction } from '../actions/getCaseAction'; +import { isLoggedInAction } from '../actions/isLoggedInAction'; +import { redirectToCognitoAction } from '../actions/redirectToCognitoAction'; +import { setCaseAction } from '../actions/setCaseAction'; +import { setCurrentPageAction } from '../actions/setCurrentPageAction'; +import { setEditDeficiencyStatisticFormAction } from '../actions/setEditDeficiencyStatisticFormAction'; +import { stopShowValidationAction } from '../actions/stopShowValidationAction'; + +export const gotoEditDeficiencyStatisticSequence = [ + isLoggedInAction, + { + isLoggedIn: [ + setCurrentPageAction('Interstitial'), + stopShowValidationAction, + clearFormAction, + getCaseAction, + setCaseAction, + setEditDeficiencyStatisticFormAction, + setCurrentPageAction('EditDeficiencyStatistic'), + ], + unauthorized: [redirectToCognitoAction], + }, +]; diff --git a/web-client/src/presenter/sequences/gotoEditOtherStatisticsSequence.js b/web-client/src/presenter/sequences/gotoEditOtherStatisticsSequence.js new file mode 100644 index 00000000000..77d044f949a --- /dev/null +++ b/web-client/src/presenter/sequences/gotoEditOtherStatisticsSequence.js @@ -0,0 +1,24 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { getCaseAction } from '../actions/getCaseAction'; +import { isLoggedInAction } from '../actions/isLoggedInAction'; +import { redirectToCognitoAction } from '../actions/redirectToCognitoAction'; +import { setCaseAction } from '../actions/setCaseAction'; +import { setCurrentPageAction } from '../actions/setCurrentPageAction'; +import { setEditOtherStatisticsFormAction } from '../actions/setEditOtherStatisticsFormAction'; +import { stopShowValidationAction } from '../actions/stopShowValidationAction'; + +export const gotoEditOtherStatisticsSequence = [ + isLoggedInAction, + { + isLoggedIn: [ + setCurrentPageAction('Interstitial'), + stopShowValidationAction, + clearFormAction, + getCaseAction, + setCaseAction, + setEditOtherStatisticsFormAction, + setCurrentPageAction('EditOtherStatistics'), + ], + unauthorized: [redirectToCognitoAction], + }, +]; diff --git a/web-client/src/presenter/sequences/gotoEditSavedPetitionSequence.js b/web-client/src/presenter/sequences/gotoEditSavedPetitionSequence.js deleted file mode 100644 index 143f85765e8..00000000000 --- a/web-client/src/presenter/sequences/gotoEditSavedPetitionSequence.js +++ /dev/null @@ -1,19 +0,0 @@ -import { clearFormsAction } from '../actions/clearFormsAction'; -import { getCaseAction } from '../actions/getCaseAction'; -import { setCaseAction } from '../actions/setCaseAction'; -import { setCaseOnFormAction } from '../actions/setCaseOnFormAction'; -import { setCurrentPageAction } from '../actions/setCurrentPageAction'; -import { setDocumentDetailTabAction } from '../actions/setDocumentDetailTabAction'; -import { setDocumentIdAction } from '../actions/setDocumentIdAction'; -import { setFormForCaseAction } from '../actions/setFormForCaseAction'; - -export const gotoEditSavedPetitionSequence = [ - clearFormsAction, - setDocumentIdAction, - getCaseAction, - setCaseAction, - setCaseOnFormAction, - setFormForCaseAction, - setDocumentDetailTabAction, - setCurrentPageAction('DocumentDetail'), -]; diff --git a/web-client/src/presenter/sequences/gotoMessageDetailSequence.js b/web-client/src/presenter/sequences/gotoMessageDetailSequence.js new file mode 100644 index 00000000000..64eec11e1fd --- /dev/null +++ b/web-client/src/presenter/sequences/gotoMessageDetailSequence.js @@ -0,0 +1,29 @@ +import { clearErrorAlertsAction } from '../actions/clearErrorAlertsAction'; +import { closeMobileMenuAction } from '../actions/closeMobileMenuAction'; +import { getCaseAction } from '../actions/getCaseAction'; +import { getMessageAction } from '../actions/getMessageAction'; +import { isLoggedInAction } from '../actions/isLoggedInAction'; +import { redirectToCognitoAction } from '../actions/redirectToCognitoAction'; +import { setCaseAction } from '../actions/setCaseAction'; +import { setCurrentPageAction } from '../actions/setCurrentPageAction'; +import { setMessageAction } from '../actions/setMessageAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; + +const gotoMessageDetail = showProgressSequenceDecorator([ + setCurrentPageAction('Interstitial'), + closeMobileMenuAction, + clearErrorAlertsAction, + getCaseAction, + setCaseAction, + getMessageAction, + setMessageAction, + setCurrentPageAction('MessageDetail'), +]); + +export const gotoMessageDetailSequence = [ + isLoggedInAction, + { + isLoggedIn: gotoMessageDetail, + unauthorized: [redirectToCognitoAction], + }, +]; diff --git a/web-client/src/presenter/sequences/gotoRequestAccessSequence.js b/web-client/src/presenter/sequences/gotoRequestAccessSequence.js index ead46f4dde9..9eb735c3f72 100644 --- a/web-client/src/presenter/sequences/gotoRequestAccessSequence.js +++ b/web-client/src/presenter/sequences/gotoRequestAccessSequence.js @@ -12,6 +12,7 @@ import { setCaseAction } from '../actions/setCaseAction'; import { setCaseAssociationAction } from '../actions/setCaseAssociationAction'; import { setCurrentPageAction } from '../actions/setCurrentPageAction'; import { setDefaultFileDocumentFormValuesAction } from '../actions/FileDocument/setDefaultFileDocumentFormValuesAction'; +import { setRequestAccessWizardStepActionGenerator } from '../actions/setRequestAccessWizardStepActionGenerator'; import { state } from 'cerebral'; import { stopShowValidationAction } from '../actions/stopShowValidationAction'; @@ -32,12 +33,12 @@ const gotoRequestAccess = [ { irsPractitioner: [ set(state.form.partyIrsPractitioner, true), - set(state.wizardStep, 'RequestAccess'), + setRequestAccessWizardStepActionGenerator('RequestAccess'), setCurrentPageAction('RequestAccessWizard'), ], privatePractitioner: [ set(state.form.partyPrivatePractitioner, true), - set(state.wizardStep, 'RequestAccess'), + setRequestAccessWizardStepActionGenerator('RequestAccess'), setCurrentPageAction('RequestAccessWizard'), ], }, diff --git a/web-client/src/presenter/sequences/loginWithCodeSequence.test.js b/web-client/src/presenter/sequences/loginWithCodeSequence.test.js index d8854ae7742..b842448bf5a 100644 --- a/web-client/src/presenter/sequences/loginWithCodeSequence.test.js +++ b/web-client/src/presenter/sequences/loginWithCodeSequence.test.js @@ -1,5 +1,5 @@ import { CerebralTest } from 'cerebral/test'; -import { User } from '../../../../shared/src/business/entities/User'; +import { ROLES } from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { loginWithCodeSequence } from '../sequences/loginWithCodeSequence'; import { presenter } from '../presenter-mock'; @@ -12,7 +12,7 @@ describe('loginWithCodeSequence', () => { const NEW_TOKEN = 'eyJraWQiOiJ2U2pTa3FZVkJjVkJOWk5qZ1gzWFNzcERZSjU4QmQ3OGYrSzlDSXhtck44PSIsImFsZyI6IlJTMjU2In0.eyJhdF9oYXNoIjoiRk5mZ2tQZlVmTTBRRWtuak5Ic1lWQSIsInN1YiI6Ijc0YzA2NDBjLTljYjQtNGE0Ny04OWMyLThjOGU5YmFiMmUyNiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV83dVJrRjBBeG4iLCJjb2duaXRvOnVzZXJuYW1lIjoiNzRjMDY0MGMtOWNiNC00YTQ3LTg5YzItOGM4ZTliYWIyZTI2IiwiYXVkIjoiNnR1Nmoxc3R2NXVnY3V0N2Rxc3FkdXJuOHEiLCJldmVudF9pZCI6ImQ5MGRjMGJlLTZhYjYtMTFlOS04YWI2LWMzNTYyYjI5YmEwOCIsInRva2VuX3VzZSI6ImlkIiwiYXV0aF90aW1lIjoxNTU2NTY2OTE3LCJuYW1lIjoiVGVzdCBwZXRpdGlvbnNjbGVyazEiLCJleHAiOjE1NTY1NzA1MTcsImN1c3RvbTpyb2xlIjoicGV0aXRpb25zY2xlcmsiLCJpYXQiOjE1NTY1NjY5MTcsImVtYWlsIjoicGV0aXRpb25zY2xlcmsxQGV4YW1wbGUuY29tIn0.mXE2yMgVhP_wHqpohtBHHcmL5WrxXxLB2KzvNMlldLRF-WcuGHIwhL1yXuYCp1Jobi1j823nYeXhAhPF4mzQLB6weXUga3UVT1op-KENSxvpfJJvuty2AGCBcjx6j85UDtA3KE9nx-xqWJkRpVHvfTVezMMc_v3QpmVuiPyfdO1gPCDUNiMpndVrBZW6iA6ANhMsud7IHx3R9ENauoDzohJBl_Zb1O-S34J-JjhbN6_fGKguzW8Hxwb3h-WImF_qZgKsR5B5gMzvhQhMAcSjltZI7a88L2OBwbTZjggxp5RAeju6GT_zY7xC_5vpUnJka8p4NwdLJyATi2GMXTSVDg'; const USER = { - role: User.ROLES.petitionsClerk, + role: ROLES.petitionsClerk, }; beforeAll(() => { applicationContext.getUseCases().authorizeCodeInteractor.mockReturnValue({ diff --git a/web-client/src/presenter/sequences/navigateToReviewSavedPetitionSequence.js b/web-client/src/presenter/sequences/navigateToReviewSavedPetitionSequence.js deleted file mode 100644 index b69355be4f9..00000000000 --- a/web-client/src/presenter/sequences/navigateToReviewSavedPetitionSequence.js +++ /dev/null @@ -1,21 +0,0 @@ -import { clearAlertsAction } from '../actions/clearAlertsAction'; -import { getCaseDetailFormWithComputedDatesAction } from '../actions/getCaseDetailFormWithComputedDatesAction'; -import { navigateToReviewSavedPetitionAction } from '../actions/caseDetailEdit/navigateToReviewSavedPetitionAction'; -import { setValidationAlertErrorsAction } from '../actions/setValidationAlertErrorsAction'; -import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; -import { startShowValidationAction } from '../actions/startShowValidationAction'; -import { stopShowValidationAction } from '../actions/stopShowValidationAction'; -import { validateCaseDetailAction } from '../actions/validateCaseDetailAction'; - -export const navigateToReviewSavedPetitionSequence = showProgressSequenceDecorator( - [ - startShowValidationAction, - clearAlertsAction, - getCaseDetailFormWithComputedDatesAction, - validateCaseDetailAction, - { - error: [setValidationAlertErrorsAction], - success: [stopShowValidationAction, navigateToReviewSavedPetitionAction], - }, - ], -); diff --git a/web-client/src/presenter/sequences/openConfirmDeleteCorrespondenceModalSequence.js b/web-client/src/presenter/sequences/openConfirmDeleteCorrespondenceModalSequence.js index de97dccad2d..f84ed3005bc 100644 --- a/web-client/src/presenter/sequences/openConfirmDeleteCorrespondenceModalSequence.js +++ b/web-client/src/presenter/sequences/openConfirmDeleteCorrespondenceModalSequence.js @@ -1,7 +1,9 @@ import { clearModalStateAction } from '../actions/clearModalStateAction'; +import { setCorrespondenceToDeleteAction } from '../actions/CorrespondenceDocument/setCorrespondenceToDeleteAction'; import { setShowModalFactoryAction } from '../actions/setShowModalFactoryAction'; export const openConfirmDeleteCorrespondenceModalSequence = [ clearModalStateAction, + setCorrespondenceToDeleteAction, setShowModalFactoryAction('DeleteCorrespondenceModal'), ]; diff --git a/web-client/src/presenter/sequences/openConfirmDeleteDeficiencyStatisticsModalSequence.js b/web-client/src/presenter/sequences/openConfirmDeleteDeficiencyStatisticsModalSequence.js new file mode 100644 index 00000000000..c37d8396994 --- /dev/null +++ b/web-client/src/presenter/sequences/openConfirmDeleteDeficiencyStatisticsModalSequence.js @@ -0,0 +1,7 @@ +import { clearModalStateAction } from '../actions/clearModalStateAction'; +import { setShowModalFactoryAction } from '../actions/setShowModalFactoryAction'; + +export const openConfirmDeleteDeficiencyStatisticsModalSequence = [ + clearModalStateAction, + setShowModalFactoryAction('ConfirmDeleteDeficiencyStatisticsModal'), +]; diff --git a/web-client/src/presenter/sequences/openConfirmDeleteOtherStatisticsModalSequence.js b/web-client/src/presenter/sequences/openConfirmDeleteOtherStatisticsModalSequence.js new file mode 100644 index 00000000000..efda14cfd83 --- /dev/null +++ b/web-client/src/presenter/sequences/openConfirmDeleteOtherStatisticsModalSequence.js @@ -0,0 +1,7 @@ +import { clearModalStateAction } from '../actions/clearModalStateAction'; +import { setShowModalFactoryAction } from '../actions/setShowModalFactoryAction'; + +export const openConfirmDeleteOtherStatisticsModalSequence = [ + clearModalStateAction, + setShowModalFactoryAction('ConfirmDeleteOtherStatisticsModal'), +]; diff --git a/web-client/src/presenter/sequences/openCreateCaseMessageModalSequence.js b/web-client/src/presenter/sequences/openCreateCaseMessageModalSequence.js new file mode 100644 index 00000000000..99fba88bde1 --- /dev/null +++ b/web-client/src/presenter/sequences/openCreateCaseMessageModalSequence.js @@ -0,0 +1,12 @@ +import { clearModalStateAction } from '../actions/clearModalStateAction'; +import { setCreateMessageModalDialogModalStateAction } from '../actions/WorkItem/setCreateMessageModalDialogModalStateAction'; +import { setShowModalFactoryAction } from '../actions/setShowModalFactoryAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; + +export const openCreateCaseMessageModalSequence = showProgressSequenceDecorator( + [ + clearModalStateAction, + setCreateMessageModalDialogModalStateAction, + setShowModalFactoryAction('CreateCaseMessageModal'), + ], +); diff --git a/web-client/src/presenter/sequences/public/gotoTodaysOpinionsSequence.js b/web-client/src/presenter/sequences/public/gotoTodaysOpinionsSequence.js new file mode 100644 index 00000000000..ff5f1b40f35 --- /dev/null +++ b/web-client/src/presenter/sequences/public/gotoTodaysOpinionsSequence.js @@ -0,0 +1,10 @@ +import { getTodaysOpinionsAction } from '../../actions/Public/getTodaysOpinionsAction'; +import { setCurrentPageAction } from '../../actions/setCurrentPageAction'; +import { setTodaysOpinionsAction } from '../../actions/Public/setTodaysOpinionsAction'; +import { showProgressSequenceDecorator } from '../../utilities/sequenceHelpers'; + +export const gotoTodaysOpinionsSequence = showProgressSequenceDecorator([ + getTodaysOpinionsAction, + setTodaysOpinionsAction, + setCurrentPageAction('TodaysOpinions'), +]); diff --git a/web-client/src/presenter/sequences/public/submitPublicCaseDocketNumberSearchSequence.js b/web-client/src/presenter/sequences/public/submitPublicCaseDocketNumberSearchSequence.js new file mode 100644 index 00000000000..589609411d0 --- /dev/null +++ b/web-client/src/presenter/sequences/public/submitPublicCaseDocketNumberSearchSequence.js @@ -0,0 +1,35 @@ +import { caseExistsAndIsNotSealedAction } from '../../actions/caseExistsAndIsNotSealedAction'; +import { clearSearchResultsAction } from '../../actions/AdvancedSearch/clearSearchResultsAction'; +import { clearSearchTermAction } from '../../actions/clearSearchTermAction'; +import { navigateToCaseDetailAction } from '../../actions/navigateToCaseDetailAction'; +import { set } from 'cerebral/factories'; +import { setAlertErrorAction } from '../../actions/setAlertErrorAction'; +import { setDocketNumberFromAdvancedSearchAction } from '../../actions/AdvancedSearch/setDocketNumberFromAdvancedSearchAction'; +import { setValidationErrorsAction } from '../../actions/setValidationErrorsAction'; +import { showProgressSequenceDecorator } from '../../utilities/sequenceHelpers'; +import { startShowValidationAction } from '../../actions/startShowValidationAction'; +import { state } from 'cerebral'; +import { stopShowValidationAction } from '../../actions/stopShowValidationAction'; +import { validateCaseDocketNumberSearchAction } from '../../actions/AdvancedSearch/validateCaseDocketNumberSearchAction'; + +export const submitPublicCaseDocketNumberSearchSequence = [ + clearSearchTermAction, + startShowValidationAction, + validateCaseDocketNumberSearchAction, + { + error: [ + setAlertErrorAction, + setValidationErrorsAction, + clearSearchResultsAction, + ], + success: showProgressSequenceDecorator([ + stopShowValidationAction, + setDocketNumberFromAdvancedSearchAction, + caseExistsAndIsNotSealedAction, + { + error: [set(state.searchResults, [])], + success: [navigateToCaseDetailAction], + }, + ]), + }, +]; diff --git a/web-client/src/presenter/sequences/refreshCaseSequence.js b/web-client/src/presenter/sequences/refreshCaseSequence.js deleted file mode 100644 index b84424f0fb4..00000000000 --- a/web-client/src/presenter/sequences/refreshCaseSequence.js +++ /dev/null @@ -1,7 +0,0 @@ -import { getCaseDeadlinesForCaseAction } from '../actions/CaseDeadline/getCaseDeadlinesForCaseAction'; -import { refreshCaseAction } from '../actions/refreshCaseAction'; - -export const refreshCaseSequence = [ - refreshCaseAction, - getCaseDeadlinesForCaseAction, -]; diff --git a/web-client/src/presenter/sequences/scannerShutdownSequence.js b/web-client/src/presenter/sequences/scannerShutdownSequence.js deleted file mode 100644 index b8da96924db..00000000000 --- a/web-client/src/presenter/sequences/scannerShutdownSequence.js +++ /dev/null @@ -1,3 +0,0 @@ -import { scannerShutdownAction } from '../actions/scannerShutdownAction'; - -export const scannerShutdownSequence = [scannerShutdownAction]; diff --git a/web-client/src/presenter/sequences/selectDocumentSequence.js b/web-client/src/presenter/sequences/selectDocumentSequence.js deleted file mode 100644 index 96272176856..00000000000 --- a/web-client/src/presenter/sequences/selectDocumentSequence.js +++ /dev/null @@ -1,61 +0,0 @@ -import { clearAlertsAction } from '../actions/clearAlertsAction'; -import { computeFormDateAction } from '../actions/FileDocument/computeFormDateAction'; -import { computeSecondaryFormDateAction } from '../actions/FileDocument/computeSecondaryFormDateAction'; -import { defaultSecondaryDocumentAction } from '../actions/FileDocument/defaultSecondaryDocumentAction'; -import { generateTitleAction } from '../actions/FileDocument/generateTitleAction'; -import { navigateToFileADocumentAction } from '../actions/FileDocument/navigateToFileADocumentAction'; -import { primeDoNotProceedPropAction } from '../actions/FileDocument/primeDoNotProceedPropAction'; -import { set } from 'cerebral/factories'; -import { setDefaultFileDocumentFormValuesAction } from '../actions/FileDocument/setDefaultFileDocumentFormValuesAction'; -import { setDocketNumberPropAction } from '../actions/FileDocument/setDocketNumberPropAction'; -import { setDocumentScenarioAction } from '../actions/FileDocument/setDocumentScenarioAction'; -import { setSecondaryDocumentScenarioAction } from '../actions/FileDocument/setSecondaryDocumentScenarioAction'; -import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; -import { shouldProceedAction } from '../actions/FileDocument/shouldProceedAction'; -import { startShowValidationAction } from '../actions/startShowValidationAction'; -import { state } from 'cerebral'; -import { stopShowValidationAction } from '../actions/stopShowValidationAction'; -import { validateSelectDocumentTypeAction } from '../actions/validateSelectDocumentTypeAction'; - -export const selectDocumentSequence = [ - startShowValidationAction, - computeFormDateAction, - computeSecondaryFormDateAction, - defaultSecondaryDocumentAction, - validateSelectDocumentTypeAction, - { - error: [setValidationErrorsAction], - success: [ - clearAlertsAction, - stopShowValidationAction, - setDocumentScenarioAction, - primeDoNotProceedPropAction, - set(state.screenMetadata.isDocumentTypeSelected, true), - set(state.screenMetadata.isSecondaryDocumentTypeSelected, false), - validateSelectDocumentTypeAction, - { - error: [], - success: [ - set(state.screenMetadata.isSecondaryDocumentTypeSelected, true), - setSecondaryDocumentScenarioAction, - validateSelectDocumentTypeAction, - { - error: [], - success: [ - shouldProceedAction, - { - ignore: [], - proceed: [ - generateTitleAction, - setDocketNumberPropAction, - setDefaultFileDocumentFormValuesAction, - navigateToFileADocumentAction, - ], - }, - ], - }, - ], - }, - ], - }, -]; diff --git a/web-client/src/presenter/sequences/selectSecondaryDocumentSequence.js b/web-client/src/presenter/sequences/selectSecondaryDocumentSequence.js deleted file mode 100644 index 0ea9378daa4..00000000000 --- a/web-client/src/presenter/sequences/selectSecondaryDocumentSequence.js +++ /dev/null @@ -1,18 +0,0 @@ -import { set } from 'cerebral/factories'; -import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; -import { startShowValidationAction } from '../actions/startShowValidationAction'; -import { state } from 'cerebral'; -import { stopShowValidationAction } from '../actions/stopShowValidationAction'; -import { validateSelectDocumentTypeAction } from '../actions/validateSelectDocumentTypeAction'; - -export const selectSecondaryDocumentSequence = [ - startShowValidationAction, - validateSelectDocumentTypeAction, - { - error: [setValidationErrorsAction], - success: [ - stopShowValidationAction, - set(state.screenMetadata.isSecondaryDocumentTypeSelected, true), - ], - }, -]; diff --git a/web-client/src/presenter/sequences/serveCaseToIrsSequence.js b/web-client/src/presenter/sequences/serveCaseToIrsSequence.js index 9db55f83d17..f72e2c0bdaf 100644 --- a/web-client/src/presenter/sequences/serveCaseToIrsSequence.js +++ b/web-client/src/presenter/sequences/serveCaseToIrsSequence.js @@ -1,7 +1,7 @@ import { clearModalAction } from '../actions/clearModalAction'; import { clearPdfPreviewUrlAction } from '../actions/CourtIssuedOrder/clearPdfPreviewUrlAction'; import { getServeToIrsAlertSuccessAction } from '../actions/StartCaseInternal/getServeToIrsAlertSuccessAction'; -import { navigateToCaseDetailAction } from '../actions/navigateToCaseDetailAction'; +import { navigateToDocumentQCAction } from '../actions/navigateToDocumentQCAction'; import { serveCaseToIrsAction } from '../actions/StartCaseInternal/serveCaseToIrsAction'; import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; import { setCurrentPageAction } from '../actions/setCurrentPageAction'; @@ -19,7 +19,7 @@ export const serveCaseToIrsSequence = [ getServeToIrsAlertSuccessAction, setAlertSuccessAction, setSaveAlertsForNavigationAction, - navigateToCaseDetailAction, + navigateToDocumentQCAction, ], paper: [ clearModalAction, diff --git a/web-client/src/presenter/sequences/setCanvasForPDFSigningSequence.js b/web-client/src/presenter/sequences/setCanvasForPDFSigningSequence.js deleted file mode 100644 index 078f7b7bb8f..00000000000 --- a/web-client/src/presenter/sequences/setCanvasForPDFSigningSequence.js +++ /dev/null @@ -1,3 +0,0 @@ -import { setCanvasForPDFSigningAction } from '../actions/setCanvasForPDFSigningAction'; - -export const setCanvasForPDFSigningSequence = [setCanvasForPDFSigningAction]; diff --git a/web-client/src/presenter/sequences/setCaseDetailPageTabSequence.js b/web-client/src/presenter/sequences/setCaseDetailPageTabSequence.js index bd3a74b4d17..b90d8601f93 100644 --- a/web-client/src/presenter/sequences/setCaseDetailPageTabSequence.js +++ b/web-client/src/presenter/sequences/setCaseDetailPageTabSequence.js @@ -1,7 +1,7 @@ -import { setCaseDetailPageTabAction } from '../actions/setCaseDetailPageTabAction'; +import { setCaseDetailPageTabActionGenerator } from '../actions/setCaseDetailPageTabActionGenerator'; import { setIsPrimaryTabAction } from '../actions/setIsPrimaryTabAction'; export const setCaseDetailPageTabSequence = [ - setCaseDetailPageTabAction, + setCaseDetailPageTabActionGenerator(), setIsPrimaryTabAction, ]; diff --git a/web-client/src/presenter/sequences/setCaseTypeToDisplaySequence.js b/web-client/src/presenter/sequences/setCaseTypeToDisplaySequence.js new file mode 100644 index 00000000000..6c56e7c76dc --- /dev/null +++ b/web-client/src/presenter/sequences/setCaseTypeToDisplaySequence.js @@ -0,0 +1,3 @@ +import { setCaseTypeToDisplayAction } from '../actions/setCaseTypeToDisplayAction'; + +export const setCaseTypeToDisplaySequence = [setCaseTypeToDisplayAction]; diff --git a/web-client/src/presenter/sequences/setDocumentDetailTabSequence.js b/web-client/src/presenter/sequences/setDocumentDetailTabSequence.js deleted file mode 100644 index c73615ab9a7..00000000000 --- a/web-client/src/presenter/sequences/setDocumentDetailTabSequence.js +++ /dev/null @@ -1,3 +0,0 @@ -import { setDocumentDetailTabAction } from '../actions/setDocumentDetailTabAction'; - -export const setDocumentDetailTabSequence = [setDocumentDetailTabAction]; diff --git a/web-client/src/presenter/sequences/setDocumentDetailTabSequence.test.js b/web-client/src/presenter/sequences/setDocumentDetailTabSequence.test.js deleted file mode 100644 index ef21e7c467b..00000000000 --- a/web-client/src/presenter/sequences/setDocumentDetailTabSequence.test.js +++ /dev/null @@ -1,27 +0,0 @@ -import { CerebralTest } from 'cerebral/test'; -import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; -import { presenter } from '../presenter-mock'; -import { setDocumentDetailTabSequence } from '../sequences/setDocumentDetailTabSequence'; - -describe('setDocumentDetailTabSequence', () => { - let test; - beforeAll(() => { - presenter.providers.applicationContext = applicationContext; - presenter.sequences = { - setDocumentDetailTabSequence, - }; - test = CerebralTest(presenter); - }); - it('updates the document detail tab based on props', async () => { - test.setState('documentDetail', { - tab: 'docketRecord', - }); - - await test.runSequence('setDocumentDetailTabSequence', { - tab: 'caseInfo', - }); - expect(test.getState('currentViewMetadata.documentDetail')).toMatchObject({ - tab: 'caseInfo', - }); - }); -}); diff --git a/web-client/src/presenter/sequences/setModalDialogNameSequence.js b/web-client/src/presenter/sequences/setModalDialogNameSequence.js deleted file mode 100644 index 269a769c733..00000000000 --- a/web-client/src/presenter/sequences/setModalDialogNameSequence.js +++ /dev/null @@ -1,3 +0,0 @@ -import { setShowModalAction } from '../actions/setShowModalAction'; - -export const setModalDialogNameSequence = [setShowModalAction]; diff --git a/web-client/src/presenter/sequences/setScannerSourceSequence.js b/web-client/src/presenter/sequences/setScannerSourceSequence.js deleted file mode 100644 index b85bf25a8f2..00000000000 --- a/web-client/src/presenter/sequences/setScannerSourceSequence.js +++ /dev/null @@ -1,7 +0,0 @@ -import { clearModalAction } from '../actions/clearModalAction'; -import { setScannerSourceAction } from '../actions/setScannerSourceAction'; - -export const setScannerSourceSequence = [ - setScannerSourceAction, - clearModalAction, -]; diff --git a/web-client/src/presenter/sequences/setScannerSourceSequence.test.js b/web-client/src/presenter/sequences/setScannerSourceSequence.test.js deleted file mode 100644 index 186fd4665af..00000000000 --- a/web-client/src/presenter/sequences/setScannerSourceSequence.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { CerebralTest } from 'cerebral/test'; -import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; -import { presenter } from '../presenter-mock'; -import { setScannerSourceSequence } from '../sequences/setScannerSourceSequence'; - -describe('setScannerSourceSequence', () => { - let test; - beforeAll(() => { - presenter.providers.applicationContext = applicationContext; - presenter.sequences = { - setScannerSourceSequence, - }; - test = CerebralTest(presenter); - }); - it('should set the scanner source based on props and close the selector modal', async () => { - test.setState('showModal', 'SelectScannerSourceModal'); - test.setState('scanner', {}); - - await test.runSequence('setScannerSourceSequence', { - scannerSourceName: 'Test Scanner 1', - }); - - expect(applicationContext.getScanner().setSourceByName).toHaveBeenCalled(); - expect( - applicationContext.getUseCases().setItemInteractor, - ).toHaveBeenCalled(); - expect(test.getState('modal')).toEqual({}); - }); -}); diff --git a/web-client/src/presenter/sequences/showCalculatePenaltiesModalSequence.js b/web-client/src/presenter/sequences/showCalculatePenaltiesModalSequence.js index d59e08b7aab..604638df00b 100644 --- a/web-client/src/presenter/sequences/showCalculatePenaltiesModalSequence.js +++ b/web-client/src/presenter/sequences/showCalculatePenaltiesModalSequence.js @@ -1,9 +1,11 @@ import { setDefaultPenaltiesAction } from '../actions/setDefaultPenaltiesAction'; +import { setModalTitleAction } from '../actions/setModalTitleAction'; import { setShowModalFactoryAction } from '../actions/setShowModalFactoryAction'; import { setStatisticIndexAction } from '../actions/setStatisticIndexAction'; export const showCalculatePenaltiesModalSequence = [ setStatisticIndexAction, + setModalTitleAction, setDefaultPenaltiesAction, setShowModalFactoryAction('CalculatePenaltiesModal'), ]; diff --git a/web-client/src/presenter/sequences/showMoreClosedCasesSequence.js b/web-client/src/presenter/sequences/showMoreClosedCasesSequence.js new file mode 100644 index 00000000000..2fc0d42eaed --- /dev/null +++ b/web-client/src/presenter/sequences/showMoreClosedCasesSequence.js @@ -0,0 +1,5 @@ +import { incrementCurrentPageClosedCasesAction } from '../actions/Dashboard/incrementCurrentPageClosedCasesAction'; + +export const showMoreClosedCasesSequence = [ + incrementCurrentPageClosedCasesAction, +]; diff --git a/web-client/src/presenter/sequences/showMoreOpenCasesSequence.js b/web-client/src/presenter/sequences/showMoreOpenCasesSequence.js new file mode 100644 index 00000000000..e605a4e76f7 --- /dev/null +++ b/web-client/src/presenter/sequences/showMoreOpenCasesSequence.js @@ -0,0 +1,3 @@ +import { incrementCurrentPageOpenCasesAction } from '../actions/Dashboard/incrementCurrentPageOpenCasesAction'; + +export const showMoreOpenCasesSequence = [incrementCurrentPageOpenCasesAction]; diff --git a/web-client/src/presenter/sequences/submitAddDeficiencyStatisticsSequence.js b/web-client/src/presenter/sequences/submitAddDeficiencyStatisticsSequence.js new file mode 100644 index 00000000000..4544d090697 --- /dev/null +++ b/web-client/src/presenter/sequences/submitAddDeficiencyStatisticsSequence.js @@ -0,0 +1,39 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { navigateToCaseDetailCaseInformationAction } from '../actions/navigateToCaseDetailCaseInformationAction'; +import { setAlertErrorAction } from '../actions/setAlertErrorAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; +import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; +import { setValidationAlertErrorsAction } from '../actions/setValidationAlertErrorsAction'; +import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; +import { startShowValidationAction } from '../actions/startShowValidationAction'; +import { submitAddDeficiencyStatisticsAction } from '../actions/submitAddDeficiencyStatisticsAction'; +import { validateAddDeficiencyStatisticsAction } from '../actions/validateAddDeficiencyStatisticsAction'; + +export const submitAddDeficiencyStatisticsSequence = [ + startShowValidationAction, + validateAddDeficiencyStatisticsAction, + { + error: [ + setAlertErrorAction, + setValidationErrorsAction, + setValidationAlertErrorsAction, + ], + success: [ + showProgressSequenceDecorator([ + submitAddDeficiencyStatisticsAction, + { + error: [], + success: [ + clearFormAction, + setSaveAlertsForNavigationAction, + setCaseDetailPageTabFrozenAction, + setAlertSuccessAction, + navigateToCaseDetailCaseInformationAction, + ], + }, + ]), + ], + }, +]; diff --git a/web-client/src/presenter/sequences/submitAddOtherStatisticsSequence.js b/web-client/src/presenter/sequences/submitAddOtherStatisticsSequence.js new file mode 100644 index 00000000000..73e26d06986 --- /dev/null +++ b/web-client/src/presenter/sequences/submitAddOtherStatisticsSequence.js @@ -0,0 +1,23 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { navigateToCaseDetailCaseInformationAction } from '../actions/navigateToCaseDetailCaseInformationAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; +import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; +import { submitOtherStatisticsAction } from '../actions/submitOtherStatisticsAction'; + +export const submitAddOtherStatisticsSequence = [ + showProgressSequenceDecorator([ + submitOtherStatisticsAction, + { + error: [], + success: [ + clearFormAction, + setSaveAlertsForNavigationAction, + setCaseDetailPageTabFrozenAction, + setAlertSuccessAction, + navigateToCaseDetailCaseInformationAction, + ], + }, + ]), +]; diff --git a/web-client/src/presenter/sequences/submitCaseAssociationRequestSequence.js b/web-client/src/presenter/sequences/submitCaseAssociationRequestSequence.js index 6653e71f72c..a1c03366553 100644 --- a/web-client/src/presenter/sequences/submitCaseAssociationRequestSequence.js +++ b/web-client/src/presenter/sequences/submitCaseAssociationRequestSequence.js @@ -10,6 +10,7 @@ import { setPractitionerOnFormAction } from '../actions/FileDocument/setPractiti import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; import { submitCaseAssociationRequestAction } from '../actions/FileDocument/submitCaseAssociationRequestAction'; +import { unsetRequestAccessWizardStepAction } from '../actions/unsetRequestAccessWizardStepAction'; import { uploadExternalDocumentsAction } from '../actions/FileDocument/uploadExternalDocumentsAction'; export const submitCaseAssociationRequestSequence = [ @@ -24,6 +25,7 @@ export const submitCaseAssociationRequestSequence = [ closeFileUploadStatusModalAction, getPrintableFilingReceiptSequence, getFileExternalDocumentAlertSuccessAction, + unsetRequestAccessWizardStepAction, setAlertSuccessAction, setSaveAlertsForNavigationAction, navigateToCaseDetailAction, diff --git a/web-client/src/presenter/sequences/submitEditDeficiencyStatisticSequence.js b/web-client/src/presenter/sequences/submitEditDeficiencyStatisticSequence.js new file mode 100644 index 00000000000..249459e7f45 --- /dev/null +++ b/web-client/src/presenter/sequences/submitEditDeficiencyStatisticSequence.js @@ -0,0 +1,39 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { navigateToCaseDetailCaseInformationAction } from '../actions/navigateToCaseDetailCaseInformationAction'; +import { setAlertErrorAction } from '../actions/setAlertErrorAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; +import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; +import { setValidationAlertErrorsAction } from '../actions/setValidationAlertErrorsAction'; +import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; +import { startShowValidationAction } from '../actions/startShowValidationAction'; +import { submitEditDeficiencyStatisticAction } from '../actions/submitEditDeficiencyStatisticAction'; +import { validateAddDeficiencyStatisticsAction } from '../actions/validateAddDeficiencyStatisticsAction'; + +export const submitEditDeficiencyStatisticSequence = [ + startShowValidationAction, + validateAddDeficiencyStatisticsAction, + { + error: [ + setAlertErrorAction, + setValidationErrorsAction, + setValidationAlertErrorsAction, + ], + success: [ + showProgressSequenceDecorator([ + submitEditDeficiencyStatisticAction, + { + error: [], + success: [ + clearFormAction, + setSaveAlertsForNavigationAction, + setCaseDetailPageTabFrozenAction, + setAlertSuccessAction, + navigateToCaseDetailCaseInformationAction, + ], + }, + ]), + ], + }, +]; diff --git a/web-client/src/presenter/sequences/submitEditOtherStatisticsSequence.js b/web-client/src/presenter/sequences/submitEditOtherStatisticsSequence.js new file mode 100644 index 00000000000..a2431097ca1 --- /dev/null +++ b/web-client/src/presenter/sequences/submitEditOtherStatisticsSequence.js @@ -0,0 +1,23 @@ +import { clearFormAction } from '../actions/clearFormAction'; +import { navigateToCaseDetailCaseInformationAction } from '../actions/navigateToCaseDetailCaseInformationAction'; +import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; +import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; +import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; +import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; +import { submitOtherStatisticsAction } from '../actions/submitOtherStatisticsAction'; + +export const submitEditOtherStatisticsSequence = [ + showProgressSequenceDecorator([ + submitOtherStatisticsAction, + { + error: [], + success: [ + clearFormAction, + setSaveAlertsForNavigationAction, + setCaseDetailPageTabFrozenAction, + setAlertSuccessAction, + navigateToCaseDetailCaseInformationAction, + ], + }, + ]), +]; diff --git a/web-client/src/presenter/sequences/toggleReportsMenuSequence.js b/web-client/src/presenter/sequences/toggleReportsMenuSequence.js deleted file mode 100644 index b3c1efb43b1..00000000000 --- a/web-client/src/presenter/sequences/toggleReportsMenuSequence.js +++ /dev/null @@ -1,4 +0,0 @@ -import { state } from 'cerebral'; -import { toggle } from 'cerebral/factories'; - -export const toggleReportsMenuSequence = [toggle(state.isReportsMenuOpen)]; diff --git a/web-client/src/presenter/sequences/unsetWorkQueueIsInternalSequence.js b/web-client/src/presenter/sequences/unsetWorkQueueIsInternalSequence.js deleted file mode 100644 index aefc5e88007..00000000000 --- a/web-client/src/presenter/sequences/unsetWorkQueueIsInternalSequence.js +++ /dev/null @@ -1,8 +0,0 @@ -import { navigateToDashboardAction } from '../actions/navigateToDashboardAction'; -import { set } from 'cerebral/factories'; -import { state } from 'cerebral'; - -export const unsetWorkQueueIsInternalSequence = [ - set(state.workQueueToDisplay.workQueueIsInternal, false), - navigateToDashboardAction, -]; diff --git a/web-client/src/presenter/sequences/updateAddDeficiencyFormValueSequence.js b/web-client/src/presenter/sequences/updateAddDeficiencyFormValueSequence.js new file mode 100644 index 00000000000..6b1f51179c6 --- /dev/null +++ b/web-client/src/presenter/sequences/updateAddDeficiencyFormValueSequence.js @@ -0,0 +1,7 @@ +import { clearAddDeficiencyFormValuesAction } from '../actions/clearAddDeficiencyFormValuesAction'; +import { setFormValueAction } from '../actions/setFormValueAction'; + +export const updateAddDeficiencyFormValueSequence = [ + clearAddDeficiencyFormValuesAction, + setFormValueAction, +]; diff --git a/web-client/src/presenter/sequences/updateCreateCaseMessageAttachmentsSequence.js b/web-client/src/presenter/sequences/updateCreateCaseMessageAttachmentsSequence.js new file mode 100644 index 00000000000..6a6c7634400 --- /dev/null +++ b/web-client/src/presenter/sequences/updateCreateCaseMessageAttachmentsSequence.js @@ -0,0 +1,5 @@ +import { updateCreateCaseMessageAttachmentsAction } from '../actions/updateCreateCaseMessageAttachmentsAction'; + +export const updateCreateCaseMessageAttachmentsSequence = [ + updateCreateCaseMessageAttachmentsAction, +]; diff --git a/web-client/src/presenter/sequences/updateCreateCaseMessageValueInModalSequence.js b/web-client/src/presenter/sequences/updateCreateCaseMessageValueInModalSequence.js new file mode 100644 index 00000000000..90dec5719d3 --- /dev/null +++ b/web-client/src/presenter/sequences/updateCreateCaseMessageValueInModalSequence.js @@ -0,0 +1,39 @@ +/* eslint-disable sort-keys-fix/sort-keys-fix */ +import { clearUsersAction } from '../actions/clearUsersAction'; +import { getUsersInSectionSequence } from './getUsersInSectionSequence'; +import { isChambersPathAction } from '../actions/ForwardForm/isChambersPathAction'; +import { props, state } from 'cerebral'; +import { runKeyPathAction } from '../actions/runKeyPathAction'; +import { set } from 'cerebral/factories'; + +// TODO: refactor this +export const updateCreateCaseMessageValueInModalSequence = [ + ({ props: sequenceProps }) => ({ + form: 'modal.form', + section: sequenceProps.value, + }), + runKeyPathAction, + { + toSection: [ + isChambersPathAction, + { + yes: [ + set(state.modal.showChambersSelect, true), + set(state.modal.form.toSection, ''), + set(state.modal.form.assigneeId, ''), + clearUsersAction, + ], + no: [ + set(state.modal.showChambersSelect, false), + set(state.modal.form.toSection, props.value), + ...getUsersInSectionSequence, + ], + }, + ], + chambers: [ + set(state.modal.form.toSection, props.value), + ...getUsersInSectionSequence, + ], + default: [set(state.modal.form[props.key], props.value)], + }, +]; diff --git a/web-client/src/presenter/sequences/updateStatisticsFormValueSequence.js b/web-client/src/presenter/sequences/updateStatisticsFormValueSequence.js new file mode 100644 index 00000000000..9aa84b97fcb --- /dev/null +++ b/web-client/src/presenter/sequences/updateStatisticsFormValueSequence.js @@ -0,0 +1,7 @@ +import { clearStatisticsFormValuesAction } from '../actions/clearStatisticsFormValuesAction'; +import { setFormValueAction } from '../actions/setFormValueAction'; + +export const updateStatisticsFormValueSequence = [ + setFormValueAction, + clearStatisticsFormValuesAction, +]; diff --git a/web-client/src/presenter/sequences/uploadCorrespondenceDocumentSequence.js b/web-client/src/presenter/sequences/uploadCorrespondenceDocumentSequence.js index ee4ade14bb6..7e9bf0cf6d8 100644 --- a/web-client/src/presenter/sequences/uploadCorrespondenceDocumentSequence.js +++ b/web-client/src/presenter/sequences/uploadCorrespondenceDocumentSequence.js @@ -1,22 +1,22 @@ import { clearAlertsAction } from '../actions/clearAlertsAction'; -import { getUploadCorrespondenceDocumentAlertSuccessAction } from '../actions/UploadCorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction'; +import { getUploadCorrespondenceDocumentAlertSuccessAction } from '../actions/CorrespondenceDocument/getUploadCorrespondenceDocumentAlertSuccessAction'; import { navigateToCaseDetailAction } from '../actions/navigateToCaseDetailAction'; import { openFileUploadErrorModal } from '../actions/openFileUploadErrorModal'; import { setAlertErrorAction } from '../actions/setAlertErrorAction'; import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; import { setCaseAction } from '../actions/setCaseAction'; -import { setCaseDetailPageTabAction } from '../actions/setCaseDetailPageTabAction'; +import { setCaseDetailPageTabActionGenerator } from '../actions/setCaseDetailPageTabActionGenerator'; import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; -import { setDocumentTitleFromFreeTextAction } from '../actions/UploadCorrespondenceDocument/setDocumentTitleFromFreeTextAction'; +import { setDocumentTitleFromFormAction } from '../actions/CorrespondenceDocument/setDocumentTitleFromFormAction'; import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; import { setValidationAlertErrorsAction } from '../actions/setValidationAlertErrorsAction'; import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; import { showProgressSequenceDecorator } from '../utilities/sequenceHelpers'; import { startShowValidationAction } from '../actions/startShowValidationAction'; import { stopShowValidationAction } from '../actions/stopShowValidationAction'; -import { submitCorrespondenceAction } from '../actions/UploadCorrespondenceDocument/submitCorrespondenceAction'; -import { uploadCorrespondenceFileAction } from '../actions/UploadCorrespondenceDocument/uploadCorrespondenceFileAction'; -import { validateUploadCorrespondenceDocumentAction } from '../actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction'; +import { submitCorrespondenceAction } from '../actions/CorrespondenceDocument/submitCorrespondenceAction'; +import { uploadCorrespondenceFileAction } from '../actions/CorrespondenceDocument/uploadCorrespondenceFileAction'; +import { validateUploadCorrespondenceDocumentAction } from '../actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction'; export const uploadCorrespondenceDocumentSequence = [ startShowValidationAction, @@ -34,13 +34,13 @@ export const uploadCorrespondenceDocumentSequence = [ { error: [openFileUploadErrorModal], success: [ - setDocumentTitleFromFreeTextAction, + setDocumentTitleFromFormAction, submitCorrespondenceAction, setCaseAction, getUploadCorrespondenceDocumentAlertSuccessAction, setAlertSuccessAction, setSaveAlertsForNavigationAction, - setCaseDetailPageTabAction, + setCaseDetailPageTabActionGenerator(), setCaseDetailPageTabFrozenAction, navigateToCaseDetailAction, ], diff --git a/web-client/src/presenter/sequences/uploadCourtIssuedDocumentSequence.js b/web-client/src/presenter/sequences/uploadCourtIssuedDocumentSequence.js index 4914c829bf4..25986726495 100644 --- a/web-client/src/presenter/sequences/uploadCourtIssuedDocumentSequence.js +++ b/web-client/src/presenter/sequences/uploadCourtIssuedDocumentSequence.js @@ -6,7 +6,7 @@ import { openFileUploadErrorModal } from '../actions/openFileUploadErrorModal'; import { setAlertErrorAction } from '../actions/setAlertErrorAction'; import { setAlertSuccessAction } from '../actions/setAlertSuccessAction'; import { setCaseAction } from '../actions/setCaseAction'; -import { setCaseDetailPageTabAction } from '../actions/setCaseDetailPageTabAction'; +import { setCaseDetailPageTabActionGenerator } from '../actions/setCaseDetailPageTabActionGenerator'; import { setCaseDetailPageTabFrozenAction } from '../actions/CaseDetail/setCaseDetailPageTabFrozenAction'; import { setIsPrimaryTabAction } from '../actions/setIsPrimaryTabAction'; import { setSaveAlertsForNavigationAction } from '../actions/setSaveAlertsForNavigationAction'; @@ -43,7 +43,7 @@ export const uploadCourtIssuedDocumentSequence = [ getUploadCourtIssuedDocumentAlertSuccessAction, setAlertSuccessAction, setSaveAlertsForNavigationAction, - setCaseDetailPageTabAction, + setCaseDetailPageTabActionGenerator(), setIsPrimaryTabAction, setCaseDetailPageTabFrozenAction, navigateToCaseDetailAction, diff --git a/web-client/src/presenter/sequences/validateAddDeficiencyStatisticsSequence.js b/web-client/src/presenter/sequences/validateAddDeficiencyStatisticsSequence.js new file mode 100644 index 00000000000..9ef18b5d669 --- /dev/null +++ b/web-client/src/presenter/sequences/validateAddDeficiencyStatisticsSequence.js @@ -0,0 +1,18 @@ +import { clearAlertsAction } from '../actions/clearAlertsAction'; +import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; +import { shouldValidateAction } from '../actions/shouldValidateAction'; +import { validateAddDeficiencyStatisticsAction } from '../actions/validateAddDeficiencyStatisticsAction'; + +export const validateAddDeficiencyStatisticsSequence = [ + shouldValidateAction, + { + ignore: [], + validate: [ + validateAddDeficiencyStatisticsAction, + { + error: [setValidationErrorsAction], + success: [clearAlertsAction], + }, + ], + }, +]; diff --git a/web-client/src/presenter/sequences/validateCreateCaseMessageInModalSequence.js b/web-client/src/presenter/sequences/validateCreateCaseMessageInModalSequence.js new file mode 100644 index 00000000000..258cd212ddc --- /dev/null +++ b/web-client/src/presenter/sequences/validateCreateCaseMessageInModalSequence.js @@ -0,0 +1,18 @@ +import { clearAlertsAction } from '../actions/clearAlertsAction'; +import { setValidationErrorsAction } from '../actions/setValidationErrorsAction'; +import { shouldValidateAction } from '../actions/shouldValidateAction'; +import { validateCreateCaseMessageAction } from '../actions/validateCreateCaseMessageAction'; + +export const validateCreateCaseMessageInModalSequence = [ + shouldValidateAction, + { + ignore: [], + validate: [ + validateCreateCaseMessageAction, + { + error: [setValidationErrorsAction], + success: [clearAlertsAction], + }, + ], + }, +]; diff --git a/web-client/src/presenter/sequences/validateUploadCorrespondenceDocumentSequence.js b/web-client/src/presenter/sequences/validateUploadCorrespondenceDocumentSequence.js index 6779f20054a..8d96e4f562d 100644 --- a/web-client/src/presenter/sequences/validateUploadCorrespondenceDocumentSequence.js +++ b/web-client/src/presenter/sequences/validateUploadCorrespondenceDocumentSequence.js @@ -1,7 +1,7 @@ import { clearAlertsAction } from '../actions/clearAlertsAction'; import { setValidationErrorsByFlagAction } from '../actions/WorkItem/setValidationErrorsByFlagAction'; import { shouldValidateAction } from '../actions/shouldValidateAction'; -import { validateUploadCorrespondenceDocumentAction } from '../actions/UploadCorrespondenceDocument/validateUploadCorrespondenceDocumentAction'; +import { validateUploadCorrespondenceDocumentAction } from '../actions/CorrespondenceDocument/validateUploadCorrespondenceDocumentAction'; export const validateUploadCorrespondenceDocumentSequence = [ shouldValidateAction, diff --git a/web-client/src/presenter/state-public.js b/web-client/src/presenter/state-public.js index acce421e64f..18c4b5c8c49 100644 --- a/web-client/src/presenter/state-public.js +++ b/web-client/src/presenter/state-public.js @@ -6,6 +6,7 @@ import { loadingHelper } from './computeds/loadingHelper'; import { publicAlertHelper } from './computeds/public/publicAlertHelper'; import { publicCaseDetailHeaderHelper } from './computeds/public/publicCaseDetailHeaderHelper'; import { publicCaseDetailHelper } from './computeds/public/publicCaseDetailHelper'; +import { todaysOpinionsHelper } from './computeds/public/todaysOpinionsHelper'; const helpers = { advancedDocumentSearchHelper, @@ -15,6 +16,7 @@ const helpers = { menuHelper, publicCaseDetailHeaderHelper, publicCaseDetailHelper, + todaysOpinionsHelper, }; export const state = { @@ -26,12 +28,19 @@ export const state = { showUsaBannerDetails: false, }, currentPage: 'Interstitial', + header: { + searchTerm: '', + showBetaBar: true, + showMobileMenu: false, + showUsaBannerDetails: false, + }, isPublic: true, progressIndicator: { // used for the spinner that shows when waiting for network responses waitingForResponse: false, waitingForResponseRequests: 0, }, + todaysOpinions: [], user: {}, validationErrors: {}, }; diff --git a/web-client/src/presenter/state.js b/web-client/src/presenter/state.js index 18862c7addc..ad8bed76f76 100644 --- a/web-client/src/presenter/state.js +++ b/web-client/src/presenter/state.js @@ -22,6 +22,7 @@ import { completeDocumentTypeSectionHelper } from './computeds/completeDocumentT import { confirmInitiateServiceModalHelper } from './computeds/confirmInitiateServiceModalHelper'; import { contactEditHelper } from './computeds/contactEditHelper'; import { contactsHelper } from './computeds/contactsHelper'; +import { createCaseMessageModalHelper } from './computeds/createCaseMessageModalHelper'; import { createOrderHelper } from './computeds/createOrderHelper'; import { createPractitionerUserHelper } from './computeds/createPractitionerUserHelper'; import { dashboardExternalHelper } from './computeds/dashboardExternalHelper'; @@ -31,15 +32,19 @@ import { documentSigningHelper } from './computeds/documentSigningHelper'; import { editDocketEntryHelper } from './computeds/editDocketEntryHelper'; import { editDocketEntryMetaHelper } from './computeds/editDocketEntryMetaHelper'; import { editPetitionerInformationHelper } from './computeds/editPetitionerInformationHelper'; -import { extractedDocument } from './computeds/extractDocument'; +import { editStatisticFormHelper } from './computeds/editStatisticFormHelper'; +import { externalUserCasesHelper } from './computeds/Dashboard/externalUserCasesHelper'; import { extractedPendingMessagesFromCaseDetail } from './computeds/extractPendingMessagesFromCaseDetail'; import { fileDocumentHelper } from './computeds/fileDocumentHelper'; import { fileUploadStatusHelper } from './computeds/fileUploadStatusHelper'; import { formattedCaseDetail, - formattedCases, + formattedClosedCases, + formattedOpenCases, } from './computeds/formattedCaseDetail'; import { formattedDashboardTrialSessions } from './computeds/formattedDashboardTrialSessions'; +import { formattedMessageDetail } from './computeds/formattedMessageDetail'; +import { formattedMessages } from './computeds/formattedMessages'; import { formattedPendingItems } from './computeds/formattedPendingItems'; import { formattedTrialSessionDetails } from './computeds/formattedTrialSessionDetails'; import { formattedTrialSessions } from './computeds/formattedTrialSessions'; @@ -49,6 +54,7 @@ import { headerHelper } from './computeds/headerHelper'; import { internalTypesHelper } from './computeds/internalTypesHelper'; import { loadingHelper } from './computeds/loadingHelper'; import { menuHelper } from './computeds/menuHelper'; +import { messagesHelper } from './computeds/messagesHelper'; import { orderTypesHelper } from './computeds/orderTypesHelper'; import { pdfPreviewModalHelper } from './computeds/PDFPreviewModal/pdfPreviewModalHelper'; import { pdfSignerHelper } from './computeds/pdfSignerHelper'; @@ -58,13 +64,13 @@ import { requestAccessHelper } from './computeds/requestAccessHelper'; import { reviewSavedPetitionHelper } from './computeds/reviewSavedPetitionHelper'; import { scanBatchPreviewerHelper } from './computeds/scanBatchPreviewerHelper'; import { scanHelper } from './computeds/scanHelper'; -import { selectDocumentSelectHelper } from './computeds/selectDocumentSelectHelper'; import { selectDocumentTypeHelper } from './computeds/selectDocumentTypeHelper'; import { showAppTimeoutModalHelper } from './computeds/showAppTimeoutModalHelper'; import { startCaseHelper } from './computeds/startCaseHelper'; import { startCaseInternalContactsHelper } from './computeds/startCaseInternalContactsHelper'; import { startCaseInternalHelper } from './computeds/startCaseInternalHelper'; import { statisticsFormHelper } from './computeds/statisticsFormHelper'; +import { statisticsHelper } from './computeds/statisticsHelper'; import { trialCitiesHelper } from './computeds/trialCitiesHelper'; import { trialSessionDetailsHelper } from './computeds/trialSessionDetailsHelper'; import { trialSessionHeaderHelper } from './computeds/trialSessionHeaderHelper'; @@ -101,6 +107,7 @@ const helpers = { confirmInitiateServiceModalHelper, contactEditHelper, contactsHelper, + createCaseMessageModalHelper, createOrderHelper, createPractitionerUserHelper, dashboardExternalHelper, @@ -110,13 +117,17 @@ const helpers = { editDocketEntryHelper, editDocketEntryMetaHelper, editPetitionerInformationHelper, - extractedDocument, + editStatisticFormHelper, + externalUserCasesHelper, extractedPendingMessagesFromCaseDetail, fileDocumentHelper, fileUploadStatusHelper, formattedCaseDetail, - formattedCases, + formattedClosedCases, formattedDashboardTrialSessions, + formattedMessageDetail, + formattedMessages, + formattedOpenCases, formattedPendingItems, formattedTrialSessionDetails, formattedTrialSessions, @@ -126,6 +137,7 @@ const helpers = { internalTypesHelper, loadingHelper, menuHelper, + messagesHelper, orderTypesHelper, pdfPreviewModalHelper, pdfSignerHelper, @@ -135,13 +147,13 @@ const helpers = { reviewSavedPetitionHelper, scanBatchPreviewerHelper, scanHelper, - selectDocumentSelectHelper, selectDocumentTypeHelper, showAppTimeoutModalHelper, startCaseHelper, startCaseInternalContactsHelper, startCaseInternalHelper, statisticsFormHelper, + statisticsHelper, trialCitiesHelper, trialSessionDetailsHelper, trialSessionHeaderHelper, @@ -165,9 +177,10 @@ export const baseState = { assigneeId: null, // used for assigning workItems in assignSelectedWorkItemsAction batchDownloads: {}, // batch download of PDFs caseDetail: {}, - cases: [], + closedCases: [], cognitoLoginUrl: null, - completeForm: {}, // TODO: replace with state.form + completeForm: {}, + // TODO: replace with state.form currentPage: 'Interstitial', currentViewMetadata: { caseDetail: { @@ -194,16 +207,19 @@ export const baseState = { tab: null, }, }, - docketRecordIndex: 0, // needs its own object because it's present when other forms are on screen + docketRecordIndex: 0, + // needs its own object because it's present when other forms are on screen documentId: null, - fieldOrder: [], // TODO: related to errors + fieldOrder: [], + // TODO: related to errors fileUploadProgress: { // used for the progress bar shown in modal when uploading files isUploading: false, percentComplete: 0, timeRemaining: Number.POSITIVE_INFINITY, }, - form: {}, // shared object for creating new entities, clear before using + form: {}, + // shared object for creating new entities, clear before using header: { searchTerm: '', showBetaBar: true, @@ -216,6 +232,7 @@ export const baseState = { }, navigation: {}, notifications: {}, + openCases: [], pdfForSigning: { documentId: null, nameForSigning: '', diff --git a/web-client/src/presenter/utilities/filterQcItemsByAssociatedJudge.test.js b/web-client/src/presenter/utilities/filterQcItemsByAssociatedJudge.test.js index 2e8e82e4b4a..a3a9c4f06a4 100644 --- a/web-client/src/presenter/utilities/filterQcItemsByAssociatedJudge.test.js +++ b/web-client/src/presenter/utilities/filterQcItemsByAssociatedJudge.test.js @@ -1,5 +1,7 @@ -import { Case } from '../../../../shared/src/business/entities/cases/Case'; -import { User } from '../../../../shared/src/business/entities/User'; +import { + CHIEF_JUDGE, + ROLES, +} from '../../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../../shared/src/business/test/createTestApplicationContext'; import { filterQcItemsByAssociatedJudge } from './filterQcItemsByAssociatedJudge'; @@ -11,8 +13,8 @@ describe('filterQcItemsByAssociatedJudge', () => { beforeAll(() => { applicationContext.getCurrentUser.mockImplementation(() => currentUser); applicationContext.getConstants.mockReturnValue({ - CHIEF_JUDGE: Case.CHIEF_JUDGE, - USER_ROLES: User.ROLES, + CHIEF_JUDGE: CHIEF_JUDGE, + USER_ROLES: ROLES, }); }); @@ -40,7 +42,7 @@ describe('filterQcItemsByAssociatedJudge', () => { it('returns a pass-through filter if the user is not of the judge, chambers, or adc roles', () => { currentUser = { - role: User.ROLES.docketClerk, + role: ROLES.docketClerk, }; const filterFunction = filterQcItemsByAssociatedJudge({ @@ -56,7 +58,7 @@ describe('filterQcItemsByAssociatedJudge', () => { it('returns a filter for the current judge or chambers user', () => { currentUser = { - role: User.ROLES.chambers, + role: ROLES.chambers, }; judgeUser = { @@ -75,7 +77,7 @@ describe('filterQcItemsByAssociatedJudge', () => { it('returns a filter for the current adc user', () => { currentUser = { - role: User.ROLES.adc, + role: ROLES.adc, }; const filterFunction = filterQcItemsByAssociatedJudge({ diff --git a/web-client/src/router.js b/web-client/src/router.js index 44edf755f73..53c5237efbf 100644 --- a/web-client/src/router.js +++ b/web-client/src/router.js @@ -82,6 +82,47 @@ const router = { }), ); + registerRoute( + '/case-detail/*/add-deficiency-statistics', + ifHasAccess(docketNumber => { + setPageTitle(`Docket ${docketNumber}`); + return app.getSequence('gotoAddDeficiencyStatisticsSequence')({ + docketNumber, + }); + }), + ); + + registerRoute( + '/case-detail/*/edit-deficiency-statistic/*', + ifHasAccess((docketNumber, statisticId) => { + setPageTitle(`Docket ${docketNumber}`); + return app.getSequence('gotoEditDeficiencyStatisticSequence')({ + docketNumber, + statisticId, + }); + }), + ); + + registerRoute( + '/case-detail/*/add-other-statistics', + ifHasAccess(docketNumber => { + setPageTitle(`Docket ${docketNumber}`); + return app.getSequence('gotoAddOtherStatisticsSequence')({ + docketNumber, + }); + }), + ); + + registerRoute( + '/case-detail/*/edit-other-statistics', + ifHasAccess(docketNumber => { + setPageTitle(`Docket ${docketNumber}`); + return app.getSequence('gotoEditOtherStatisticsSequence')({ + docketNumber, + }); + }), + ); + registerRoute( '/case-detail/*?openModal=*', ifHasAccess((docketNumber, openModal) => { @@ -438,6 +479,19 @@ const router = { }), ); + registerRoute( + '/case-detail/*/edit-correspondence/*', + ifHasAccess((docketNumber, documentId) => { + setPageTitle( + `${getPageTitleDocketPrefix(docketNumber)} Edit Correspondence`, + ); + return app.getSequence('gotoEditCorrespondenceDocumentSequence')({ + docketNumber, + documentId, + }); + }), + ); + registerRoute( '/case-detail/*/edit-order/*', ifHasAccess((docketNumber, documentIdToEdit) => { @@ -925,6 +979,28 @@ const router = { }), ); + registerRoute( + '/case-messages/*/*', + ifHasAccess((queue, box) => { + setPageTitle('Messages'); + return app.getSequence('gotoCaseMessagesSequence')({ + box, + queue, + }); + }), + ); + + registerRoute( + '/case-messages/*/message-detail/*', + ifHasAccess((docketNumber, messageId) => { + setPageTitle('Message detail'); + return app.getSequence('gotoMessageDetailSequence')({ + docketNumber, + messageId, + }); + }), + ); + registerRoute( '/pdf-preview', ifHasAccess(() => { diff --git a/web-client/src/routerPublic.js b/web-client/src/routerPublic.js index 945cb45ec26..176ffc502b6 100644 --- a/web-client/src/routerPublic.js +++ b/web-client/src/routerPublic.js @@ -23,11 +23,6 @@ const router = { initialize: app => { document.title = 'U.S. Tax Court'; - route('/..', () => { - setPageTitle('Dashboard'); - app.getSequence('gotoPublicSearchSequence')(); - }); - route('/case-detail/*', docketNumber => { setPageTitle(`Docket ${docketNumber}`); app.getSequence('gotoPublicCaseDetailSequence')({ docketNumber }); @@ -40,6 +35,23 @@ const router = { }); }); + route('/todays-opinions', () => { + setPageTitle('Today’s Opinions'); + app.getSequence('gotoTodaysOpinionsSequence')(); + }); + + route('/', () => { + setPageTitle('Dashboard'); + app.getSequence('gotoPublicSearchSequence')(); + }); + + route('..', () => { + setPageTitle('Error'); + return app.getSequence('notFoundErrorSequence')({ + error: {}, + }); + }); + route.start(true); }, }; diff --git a/web-client/src/styles/buttons.scss b/web-client/src/styles/buttons.scss index 5a51b36effe..c8cbc973cca 100644 --- a/web-client/src/styles/buttons.scss +++ b/web-client/src/styles/buttons.scss @@ -103,6 +103,10 @@ svg { margin-right: 5px; } + + &.usa-button--unstyled:visited { + color: $color-white; + } } .recall-button-box { @@ -136,3 +140,9 @@ border: none; background-color: rgba(0, 0, 0, 0); } + +.add-deficiency-statistics-form .calculate-penalties { + position: absolute; + top: 50px; + left: 180px; +} diff --git a/web-client/src/styles/custom.scss b/web-client/src/styles/custom.scss index 91aabbd29cf..de4b4abf9fd 100644 --- a/web-client/src/styles/custom.scss +++ b/web-client/src/styles/custom.scss @@ -1493,10 +1493,16 @@ button.change-scanner-button { font-size: 17px; } -.upload-court-document-description-container { - min-height: 471px; +.desktop\:upload-court-document-description-container { + @media only screen and (min-width: $medium-screen) { + min-height: 471px; + } } .accordion-item-title { align-self: center; } + +.message-detail--attachments { + height: 1000px; +} diff --git a/web-client/src/styles/forms.scss b/web-client/src/styles/forms.scss index 5cb131dccac..0828f2fe57f 100644 --- a/web-client/src/styles/forms.scss +++ b/web-client/src/styles/forms.scss @@ -494,3 +494,11 @@ label.ustc-upload { margin-bottom: 0; } } + +.year-small { + width: 80px; +} + +.input-medium { + width: 140px; +} diff --git a/web-client/src/styles/menus.scss b/web-client/src/styles/menus.scss index 9f4f2f11f33..4b268ed78b4 100644 --- a/web-client/src/styles/menus.scss +++ b/web-client/src/styles/menus.scss @@ -13,24 +13,31 @@ background-color: $color-white; box-shadow: 3px 9px 9px rgba(0, 0, 0, 0.2); - .usa-nav__submenu-item button { - padding: 0.2rem; - color: color($theme-color-primary); - font-weight: $font-semibold; + .usa-nav__submenu-item { + svg.fa-1x { + width: 16px; + } + + button { + padding: 0.2rem; + color: color($theme-color-primary); + font-weight: $font-semibold; - &:hover { - text-decoration: underline; + &:hover { + text-decoration: underline; + } } - } - .usa-nav__submenu-item a { - padding: 0.2rem; - background-color: $color-white; - color: color($theme-color-primary); - font-size: 0.93rem; - font-weight: $font-semibold; + a { + padding: 0.2rem; + background-color: $color-white; + color: color($theme-color-primary); + font-size: 0.93rem; + font-weight: $font-semibold; + } } + &.position-right-0 { right: 0; } diff --git a/web-client/src/styles/overrides.scss b/web-client/src/styles/overrides.scss index 46a972cd896..e6782ae648d 100644 --- a/web-client/src/styles/overrides.scss +++ b/web-client/src/styles/overrides.scss @@ -95,6 +95,10 @@ option:first-child { margin-bottom: 20px; } +.margin-bottom-20 { + margin-bottom: 20px; +} + .usa-error-message { margin: 15px 0 0 0; font-size: 17px; diff --git a/web-client/src/ustc-ui/Tabs/Tabs.cerebral.test.js b/web-client/src/ustc-ui/Tabs/Tabs.cerebral.test.js index 96a892e0c48..df47da0c87e 100644 --- a/web-client/src/ustc-ui/Tabs/Tabs.cerebral.test.js +++ b/web-client/src/ustc-ui/Tabs/Tabs.cerebral.test.js @@ -16,18 +16,22 @@ describe('TabsComponent', () => { }, }; const app = App(testModule); - const testRenderer = TestRenderer.create( - - - -
Section
-
- -
Indy
-
-
-
, - ); + + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + + + +
Section
+
+ +
Indy
+
+
+
, + ); + }); const testInstance = testRenderer.root; @@ -50,18 +54,21 @@ describe('TabsComponent', () => { }, }; const app = App(testModule); - const testRenderer = TestRenderer.create( - - - -
Section
-
- -
Indy
-
-
-
, - ); + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + + + +
Section
+
+ +
Indy
+
+
+
, + ); + }); const testInstance = testRenderer.root; @@ -92,18 +99,21 @@ describe('TabsComponent', () => { }, }; const app = App(testModule); - const testRenderer = TestRenderer.create( - - - -
Section
-
- -
Indy
-
-
-
, - ); + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + + + +
Section
+
+ +
Indy
+
+
+
, + ); + }); const testInstance = testRenderer.root; diff --git a/web-client/src/ustc-ui/Tabs/Tabs.jsx b/web-client/src/ustc-ui/Tabs/Tabs.jsx index b471f48b4c0..67a3fdd2a19 100644 --- a/web-client/src/ustc-ui/Tabs/Tabs.jsx +++ b/web-client/src/ustc-ui/Tabs/Tabs.jsx @@ -8,6 +8,7 @@ import { getDefaultAttribute, map } from '../utils/ElementChildren'; import { props, sequences, state } from 'cerebral'; import React, { useState } from 'react'; + import classNames from 'classnames'; let FontAwesomeIcon; diff --git a/web-client/src/ustc-ui/Tabs/Tabs.test.js b/web-client/src/ustc-ui/Tabs/Tabs.test.js index ae53123ec76..1590ad04c07 100644 --- a/web-client/src/ustc-ui/Tabs/Tabs.test.js +++ b/web-client/src/ustc-ui/Tabs/Tabs.test.js @@ -5,16 +5,19 @@ import { Tab, TabsComponent } from './Tabs'; describe('TabsComponent', () => { it('should show the default item', () => { - const testRenderer = TestRenderer.create( - - -
Indy
-
- -
Section
-
-
, - ); + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + + +
Indy
+
+ +
Section
+
+
, + ); + }); const testInstance = testRenderer.root; @@ -28,17 +31,19 @@ describe('TabsComponent', () => { }); it('should be able to click to show an item', () => { - const testRenderer = TestRenderer.create( - - -
Indy
-
- -
Section
-
-
, - ); - + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + + +
Indy
+
+ +
Section
+
+
, + ); + }); const testInstance = testRenderer.root; expect( @@ -59,16 +64,19 @@ describe('TabsComponent', () => { }); it('should show the selected item from bind/value', () => { - const testRenderer = TestRenderer.create( - v.value} value="section"> - -
Indy
-
- -
Section
-
-
, - ); + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + v.value} value="section"> + +
Indy
+
+ +
Section
+
+
, + ); + }); const testInstance = testRenderer.root; expect( @@ -81,11 +89,14 @@ describe('TabsComponent', () => { }); it('should show non tab content', () => { - const testRenderer = TestRenderer.create( - -
Non Tab
-
, - ); + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + +
Non Tab
+
, + ); + }); const testInstance = testRenderer.root; @@ -95,17 +106,20 @@ describe('TabsComponent', () => { }); it('should not show tab for items without title (for tab-less tabs)', () => { - const testRenderer = TestRenderer.create( - - -
Indy
-
- -
Section
-
-
Non Tab
-
, - ); + let testRenderer; + act(() => { + testRenderer = TestRenderer.create( + + +
Indy
+
+ +
Section
+
+
Non Tab
+
, + ); + }); const testInstance = testRenderer.root; diff --git a/web-client/src/ustc-ui/utils/limitLength.js b/web-client/src/ustc-ui/utils/limitLength.js deleted file mode 100644 index 632f51d63e2..00000000000 --- a/web-client/src/ustc-ui/utils/limitLength.js +++ /dev/null @@ -1,3 +0,0 @@ -export const limitLength = (value, length) => { - return value.slice(0, length); -}; diff --git a/web-client/src/ustc-ui/utils/limitLength.test.js b/web-client/src/ustc-ui/utils/limitLength.test.js deleted file mode 100644 index 6fcf2e005ee..00000000000 --- a/web-client/src/ustc-ui/utils/limitLength.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { limitLength } from './limitLength'; - -describe('limitLength', () => { - it('should shorten 123 to 12', () => { - const result = limitLength('123', 2); - expect(result).toEqual('12'); - }); - it('should shorten 1 to 1', () => { - const result = limitLength('1', 2); - expect(result).toEqual('1'); - }); -}); diff --git a/web-client/src/utilities/getScanModeLabel.test.js b/web-client/src/utilities/getScanModeLabel.test.js index 37e1ef89bbe..194a900a754 100644 --- a/web-client/src/utilities/getScanModeLabel.test.js +++ b/web-client/src/utilities/getScanModeLabel.test.js @@ -1,10 +1,8 @@ -import { Scan } from '../../../shared/src/business/entities/Scan'; +import { SCAN_MODES } from '../../../shared/src/business/entities/EntityConstants'; import { applicationContextForClient as applicationContext } from '../../../shared/src/business/test/createTestApplicationContext'; import { getScanModeLabel } from './getScanModeLabel'; describe('getScanModeLabel', () => { - const { SCAN_MODES } = Scan; - it('Returns Single Sided when the scan mode is feeder', () => { expect(getScanModeLabel(applicationContext, SCAN_MODES.FEEDER)).toEqual( 'Single sided', diff --git a/web-client/src/views/AdvancedSearch/AdvancedDocumentSearch.jsx b/web-client/src/views/AdvancedSearch/AdvancedDocumentSearch.jsx index 38fe6021e61..184480df374 100644 --- a/web-client/src/views/AdvancedSearch/AdvancedDocumentSearch.jsx +++ b/web-client/src/views/AdvancedSearch/AdvancedDocumentSearch.jsx @@ -47,7 +47,7 @@ export const AdvancedDocumentSearch = connect( onChange={e => { updateSequence({ key: e.target.name, - value: e.target.value, + value: e.target.value.toUpperCase(), }); }} /> @@ -205,7 +205,7 @@ export const AdvancedDocumentSearch = connect( onChange={e => { updateSequence({ key: e.target.name, - value: e.target.value, + value: e.target.value.toUpperCase(), }); }} /> diff --git a/web-client/src/views/AdvancedSearch/AdvancedSearch.jsx b/web-client/src/views/AdvancedSearch/AdvancedSearch.jsx index d91429ba859..c1dff1a7ebc 100644 --- a/web-client/src/views/AdvancedSearch/AdvancedSearch.jsx +++ b/web-client/src/views/AdvancedSearch/AdvancedSearch.jsx @@ -17,6 +17,7 @@ export const AdvancedSearch = connect( { advancedSearchHelper: state.advancedSearchHelper, advancedSearchTabChangeSequence: sequences.advancedSearchTabChangeSequence, + searchTabs: state.constants.ADVANCED_SEARCH_TABS, submitCaseAdvancedSearchSequence: sequences.submitCaseAdvancedSearchSequence, submitCaseDocketNumberSearchSequence: @@ -33,6 +34,7 @@ export const AdvancedSearch = connect( function AdvancedSearch({ advancedSearchHelper, advancedSearchTabChangeSequence, + searchTabs, submitCaseAdvancedSearchSequence, submitCaseDocketNumberSearchSequence, submitOpinionAdvancedSearchSequence, @@ -50,12 +52,12 @@ export const AdvancedSearch = connect( { advancedSearchTabChangeSequence(); }} > - +

Anyone can search for a case in our system for cases filed{' '} on or after May 1, 1986. @@ -71,13 +73,13 @@ export const AdvancedSearch = connect( /> - + - + diff --git a/web-client/src/views/AdvancedSearch/DocumentSearchResults.jsx b/web-client/src/views/AdvancedSearch/DocumentSearchResults.jsx index b9273e17544..10ffe8591ef 100644 --- a/web-client/src/views/AdvancedSearch/DocumentSearchResults.jsx +++ b/web-client/src/views/AdvancedSearch/DocumentSearchResults.jsx @@ -9,14 +9,12 @@ export const DocumentSearchResults = connect( { advancedDocumentSearchHelper: state.advancedDocumentSearchHelper, baseUrl: state.baseUrl, - pageSize: state.constants.CASE_SEARCH_PAGE_SIZE, showMoreResultsSequence: sequences.showMoreResultsSequence, token: state.token, }, function DocumentSearchResults({ advancedDocumentSearchHelper, baseUrl, - pageSize, showMoreResultsSequence, token, }) { @@ -35,7 +33,7 @@ export const DocumentSearchResults = connect( Docket number Case title - Order + {advancedDocumentSearchHelper.documentTypeVerbiage} Pages Date Judge @@ -49,14 +47,15 @@ export const DocumentSearchResults = connect( {idx + 1} - {result.isSealed && ( - - )} + {advancedDocumentSearchHelper.showSealedIcon && + result.isSealed && ( + + )} @@ -91,10 +90,10 @@ export const DocumentSearchResults = connect( {advancedDocumentSearchHelper.showLoadMore && ( )} {advancedDocumentSearchHelper.showNoMatches && ( diff --git a/web-client/src/views/AdvancedSearch/PractitionerSearchResults.jsx b/web-client/src/views/AdvancedSearch/PractitionerSearchResults.jsx index df15ff483de..8a5971fc08a 100644 --- a/web-client/src/views/AdvancedSearch/PractitionerSearchResults.jsx +++ b/web-client/src/views/AdvancedSearch/PractitionerSearchResults.jsx @@ -6,12 +6,10 @@ import React from 'react'; export const PractitionerSearchResults = connect( { advancedSearchHelper: state.advancedSearchHelper, - pageSize: state.constants.CASE_SEARCH_PAGE_SIZE, showMoreResultsSequence: sequences.showMoreResultsSequence, }, function PractitionerSearchResults({ advancedSearchHelper, - pageSize, showMoreResultsSequence, }) { return ( @@ -52,7 +50,7 @@ export const PractitionerSearchResults = connect( )} {advancedSearchHelper.showLoadMore && ( )} {advancedSearchHelper.showNoMatches && ( diff --git a/web-client/src/views/AdvancedSearch/SearchResults.jsx b/web-client/src/views/AdvancedSearch/SearchResults.jsx index 79b53e4e0ab..5428aa59fbf 100644 --- a/web-client/src/views/AdvancedSearch/SearchResults.jsx +++ b/web-client/src/views/AdvancedSearch/SearchResults.jsx @@ -8,14 +8,9 @@ import React from 'react'; export const SearchResults = connect( { advancedSearchHelper: state.advancedSearchHelper, - pageSize: state.constants.CASE_SEARCH_PAGE_SIZE, showMoreResultsSequence: sequences.showMoreResultsSequence, }, - function SearchResults({ - advancedSearchHelper, - pageSize, - showMoreResultsSequence, - }) { + function SearchResults({ advancedSearchHelper, showMoreResultsSequence }) { return ( <> {advancedSearchHelper.showSearchResults && ( @@ -91,7 +86,7 @@ export const SearchResults = connect( )} {advancedSearchHelper.showLoadMore && ( )} {advancedSearchHelper.showNoMatches && ( diff --git a/web-client/src/views/AppComponent.jsx b/web-client/src/views/AppComponent.jsx index f1bf8c8c61b..4e95b4110d6 100644 --- a/web-client/src/views/AppComponent.jsx +++ b/web-client/src/views/AppComponent.jsx @@ -1,5 +1,7 @@ import { AccessibilityStatement } from './Accessibility/AccessibilityStatement'; +import { AddDeficiencyStatistics } from './CaseDetail/AddDeficiencyStatistics'; import { AddDocketEntry } from './AddDocketEntry/AddDocketEntry'; +import { AddOtherStatistics } from './CaseDetail/AddOtherStatistics'; import { AddTrialSession } from './TrialSessions/AddTrialSession'; import { AdvancedSearch } from './AdvancedSearch/AdvancedSearch'; import { BatchDownloadProgress } from './TrialSessionWorkingCopy/BatchDownloadProgress'; @@ -11,6 +13,7 @@ import { CaseDetail } from './CaseDetail/CaseDetail'; import { CaseDetailInternal } from './CaseDetail/CaseDetailInternal'; import { CaseInventoryReport } from './CaseInventoryReport/CaseInventoryReport'; import { CaseInventoryReportModal } from './CaseInventoryReport/CaseInventoryReportModal'; +import { CaseMessages } from './Messages/CaseMessages'; import { CaseSearchNoMatches } from './CaseSearchNoMatches'; import { CourtIssuedDocketEntry } from './CourtIssuedDocketEntry/CourtIssuedDocketEntry'; import { CreateOrder } from './CreateOrder/CreateOrder'; @@ -23,8 +26,11 @@ import { DashboardPetitioner } from './Dashboards/DashboardPetitioner'; import { DashboardPractitioner } from './Dashboards/DashboardPractitioner'; import { DashboardRespondent } from './Dashboards/DashboardRespondent'; import { DocumentDetail } from './DocumentDetail/DocumentDetail'; +import { EditCorrespondenceDocument } from './Correspondence/EditCorrespondenceDocument'; +import { EditDeficiencyStatistic } from './CaseDetail/EditDeficiencyStatistic'; import { EditDocketEntry } from './EditDocketEntry/EditDocketEntry'; import { EditDocketEntryMeta } from './EditDocketEntry/EditDocketEntryMeta'; +import { EditOtherStatistics } from './CaseDetail/EditOtherStatistics'; import { EditPetitionDetails } from './CaseDetail/EditPetitionDetails'; import { EditPetitionerInformation } from './CaseDetail/EditPetitionerInformation'; import { EditPractitionerUser } from './Practitioners/EditPractitionerUser'; @@ -40,6 +46,7 @@ import { IdleLogout } from './IdleLogout'; import { Interstitial } from './Interstitial'; import { Loading } from './Loading'; import { LogIn } from './LogIn'; +import { MessageDetail } from './Messages/MessageDetail'; import { Messages } from './Messages/Messages'; import { PendingReport } from './PendingReport/PendingReport'; import { PetitionQc } from './PetitionQc/PetitionQc'; @@ -75,7 +82,9 @@ import React, { useEffect } from 'react'; const pages = { AccessibilityStatement, + AddDeficiencyStatistics, AddDocketEntry, + AddOtherStatistics, AddTrialSession, AdvancedSearch, BeforeStartingCase, @@ -85,6 +94,7 @@ const pages = { CaseDetail, CaseDetailInternal, CaseInventoryReport, + CaseMessages, CaseSearchNoMatches, CourtIssuedDocketEntry, CreateOrder, @@ -97,8 +107,11 @@ const pages = { DashboardPractitioner, DashboardRespondent, DocumentDetail, + EditCorrespondenceDocument, + EditDeficiencyStatistic, EditDocketEntry, EditDocketEntryMeta, + EditOtherStatistics, EditPetitionDetails, EditPetitionerInformation, EditPractitionerUser, @@ -111,6 +124,7 @@ const pages = { Interstitial, Loading, LogIn, + MessageDetail, Messages, PendingReport, PetitionQc, @@ -159,7 +173,7 @@ export const AppComponent = connect( useEffect(() => { focusMain(); - }); + }, [currentPage]); const CurrentPage = pages[currentPage]; return ( diff --git a/web-client/src/views/AppComponentPublic.jsx b/web-client/src/views/AppComponentPublic.jsx index 22ef69da456..5fbb87c099b 100644 --- a/web-client/src/views/AppComponentPublic.jsx +++ b/web-client/src/views/AppComponentPublic.jsx @@ -6,6 +6,7 @@ import { Loading } from './Loading'; import { PublicCaseDetail } from './Public/PublicCaseDetail'; import { PublicPrintableDocketRecord } from './Public/PublicPrintableDocketRecord'; import { PublicSearch } from './Public/PublicSearch'; +import { TodaysOpinions } from './Public/TodaysOpinions'; import { UsaBanner } from './UsaBanner'; import { connect } from '@cerebral/react'; import { state } from 'cerebral'; @@ -17,6 +18,7 @@ const pages = { PublicCaseDetail, PublicPrintableDocketRecord, PublicSearch, + TodaysOpinions, }; /** diff --git a/web-client/src/views/CaseDetail/AddDeficiencyStatistics.jsx b/web-client/src/views/CaseDetail/AddDeficiencyStatistics.jsx new file mode 100644 index 00000000000..b7a810485fb --- /dev/null +++ b/web-client/src/views/CaseDetail/AddDeficiencyStatistics.jsx @@ -0,0 +1,68 @@ +import { Button } from '../../ustc-ui/Button/Button'; +import { CalculatePenaltiesModal } from '../StartCaseInternal/CalculatePenaltiesModal'; +import { CaseDetailHeader } from './CaseDetailHeader'; +import { DeficiencyStatisticsForm } from './DeficiencyStatisticsForm'; +import { ErrorNotification } from '../ErrorNotification'; +import { SuccessNotification } from '../SuccessNotification'; +import { connect } from '@cerebral/react'; +import { sequences, state } from 'cerebral'; +import React from 'react'; + +export const AddDeficiencyStatistics = connect( + { + calculatePenaltiesForAddSequence: + sequences.calculatePenaltiesForAddSequence, + cancelAddStatisticSequence: sequences.cancelAddStatisticSequence, + showModal: state.modal.showModal, + submitAddDeficiencyStatisticsSequence: + sequences.submitAddDeficiencyStatisticsSequence, + validateAddDeficiencyStatisticsSequence: + sequences.validateAddDeficiencyStatisticsSequence, + }, + function AddDeficiencyStatistics({ + calculatePenaltiesForAddSequence, + cancelAddStatisticSequence, + showModal, + submitAddDeficiencyStatisticsSequence, + validateAddDeficiencyStatisticsSequence, + }) { + return ( + <> + + +

+ + + +

Add Year/Period

+ +
+ +
+ +
+ + + +
+
+ {showModal === 'CalculatePenaltiesModal' && ( + { + await calculatePenaltiesForAddSequence(); + await validateAddDeficiencyStatisticsSequence(); + }} + /> + )} + + ); + }, +); diff --git a/web-client/src/views/CaseDetail/AddOtherStatistics.jsx b/web-client/src/views/CaseDetail/AddOtherStatistics.jsx new file mode 100644 index 00000000000..e5eebc13b58 --- /dev/null +++ b/web-client/src/views/CaseDetail/AddOtherStatistics.jsx @@ -0,0 +1,51 @@ +import { Button } from '../../ustc-ui/Button/Button'; +import { CaseDetailHeader } from './CaseDetailHeader'; +import { ErrorNotification } from '../ErrorNotification'; +import { OtherStatisticsForm } from './OtherStatisticsForm'; +import { SuccessNotification } from '../SuccessNotification'; +import { connect } from '@cerebral/react'; +import { sequences } from 'cerebral'; +import React from 'react'; + +export const AddOtherStatistics = connect( + { + cancelAddStatisticSequence: sequences.cancelAddStatisticSequence, + submitAddOtherStatisticsSequence: + sequences.submitAddOtherStatisticsSequence, + }, + function AddOtherStatistics({ + cancelAddStatisticSequence, + submitAddOtherStatisticsSequence, + }) { + return ( + <> + + +
+ + + +

Add Other Statistics

+ +
+ +
+ +
+ + + +
+
+ + ); + }, +); diff --git a/web-client/src/views/CaseDetail/CaseDetailHeaderMenu.jsx b/web-client/src/views/CaseDetail/CaseDetailHeaderMenu.jsx index ffae81457df..08665aabeb5 100644 --- a/web-client/src/views/CaseDetail/CaseDetailHeaderMenu.jsx +++ b/web-client/src/views/CaseDetail/CaseDetailHeaderMenu.jsx @@ -3,6 +3,7 @@ import { AddToTrialModal } from './AddToTrialModal'; import { BlockFromTrialModal } from './BlockFromTrialModal'; import { Button } from '../../ustc-ui/Button/Button'; import { CreateCaseDeadlineModalDialog } from './CreateCaseDeadlineModalDialog'; +import { CreateCaseMessageModalDialog } from '../Messages/CreateCaseMessageModalDialog'; import { CreateOrderChooseTypeModal } from '../CreateOrder/CreateOrderChooseTypeModal'; import { DeleteCaseDeadlineModalDialog } from './DeleteCaseDeadlineModalDialog'; import { EditCaseDeadlineModalDialog } from './EditCaseDeadlineModalDialog'; @@ -26,6 +27,8 @@ export const CaseDetailHeaderMenu = connect( sequences.openAddEditCaseNoteModalSequence, openCreateCaseDeadlineModalSequence: sequences.openCreateCaseDeadlineModalSequence, + openCreateCaseMessageModalSequence: + sequences.openCreateCaseMessageModalSequence, openCreateOrderChooseTypeModalSequence: sequences.openCreateOrderChooseTypeModalSequence, openUpdateCaseModalSequence: sequences.openUpdateCaseModalSequence, @@ -39,6 +42,7 @@ export const CaseDetailHeaderMenu = connect( isCaseDetailMenuOpen, openAddEditCaseNoteModalSequence, openCreateCaseDeadlineModalSequence, + openCreateCaseMessageModalSequence, openCreateOrderChooseTypeModalSequence, openUpdateCaseModalSequence, resetCaseMenuSequence, @@ -101,6 +105,19 @@ export const CaseDetailHeaderMenu = connect( {isCaseDetailMenuOpen && (
    +
  • + +
  • +
  • + + + + +
    +
    + + + validateAddDeficiencyStatisticsSequence()} + onValueChange={values => { + updateFormValueSequence({ + key: 'determinationDeficiencyAmount', + value: values.value, + }); + }} + /> + +
    + +
    + + + validateAddDeficiencyStatisticsSequence()} + onValueChange={values => { + updateFormValueSequence({ + key: 'determinationTotalPenalties', + value: values.value, + }); + }} + /> + + +
    +
    + + + ); + }, +); diff --git a/web-client/src/views/CaseDetail/EditDeficiencyStatistic.jsx b/web-client/src/views/CaseDetail/EditDeficiencyStatistic.jsx new file mode 100644 index 00000000000..e7cf6cd92f6 --- /dev/null +++ b/web-client/src/views/CaseDetail/EditDeficiencyStatistic.jsx @@ -0,0 +1,101 @@ +import { Button } from '../../ustc-ui/Button/Button'; +import { CalculatePenaltiesModal } from '../StartCaseInternal/CalculatePenaltiesModal'; +import { CaseDetailHeader } from './CaseDetailHeader'; +import { ConfirmModal } from '../../ustc-ui/Modal/ConfirmModal'; +import { DeficiencyStatisticsForm } from './DeficiencyStatisticsForm'; +import { ErrorNotification } from '../ErrorNotification'; +import { SuccessNotification } from '../SuccessNotification'; +import { connect } from '@cerebral/react'; +import { sequences, state } from 'cerebral'; +import React from 'react'; + +export const EditDeficiencyStatistic = connect( + { + calculatePenaltiesForAddSequence: + sequences.calculatePenaltiesForAddSequence, + cancelAddStatisticSequence: sequences.cancelAddStatisticSequence, + editStatisticFormHelper: state.editStatisticFormHelper, + openConfirmDeleteDeficiencyStatisticsModalSequence: + sequences.openConfirmDeleteDeficiencyStatisticsModalSequence, + showModal: state.modal.showModal, + submitEditDeficiencyStatisticSequence: + sequences.submitEditDeficiencyStatisticSequence, + validateAddDeficiencyStatisticsSequence: + sequences.validateAddDeficiencyStatisticsSequence, + }, + function EditDeficiencyStatistic({ + calculatePenaltiesForAddSequence, + cancelAddStatisticSequence, + editStatisticFormHelper, + openConfirmDeleteDeficiencyStatisticsModalSequence, + showModal, + submitEditDeficiencyStatisticSequence, + validateAddDeficiencyStatisticsSequence, + }) { + return ( + <> + + +
    + + + +

    + Edit Year/Period - {editStatisticFormHelper.headerDateDisplay} +

    + +
    + + {editStatisticFormHelper.showDelete && ( +
    + +
    + )} +
    + +
    + + + +
    +
    + {showModal === 'CalculatePenaltiesModal' && ( + { + await calculatePenaltiesForAddSequence(); + await validateAddDeficiencyStatisticsSequence(); + }} + /> + )} + {showModal === 'ConfirmDeleteDeficiencyStatisticsModal' && ( + + Are you sure you want to delete the year/period? + + )} + + ); + }, +); diff --git a/web-client/src/views/CaseDetail/EditOtherStatistics.jsx b/web-client/src/views/CaseDetail/EditOtherStatistics.jsx new file mode 100644 index 00000000000..1b7685e7ac4 --- /dev/null +++ b/web-client/src/views/CaseDetail/EditOtherStatistics.jsx @@ -0,0 +1,79 @@ +import { Button } from '../../ustc-ui/Button/Button'; +import { CaseDetailHeader } from './CaseDetailHeader'; +import { ConfirmModal } from '../../ustc-ui/Modal/ConfirmModal'; +import { ErrorNotification } from '../ErrorNotification'; +import { OtherStatisticsForm } from './OtherStatisticsForm'; +import { SuccessNotification } from '../SuccessNotification'; +import { connect } from '@cerebral/react'; +import { sequences, state } from 'cerebral'; +import React from 'react'; + +export const EditOtherStatistics = connect( + { + cancelAddStatisticSequence: sequences.cancelAddStatisticSequence, + openConfirmDeleteOtherStatisticsModalSequence: + sequences.openConfirmDeleteOtherStatisticsModalSequence, + showModal: state.modal.showModal, + submitEditOtherStatisticsSequence: + sequences.submitEditOtherStatisticsSequence, + }, + function EditOtherStatistics({ + cancelAddStatisticSequence, + openConfirmDeleteOtherStatisticsModalSequence, + showModal, + submitEditOtherStatisticsSequence, + }) { + return ( + <> + + +
    + + + +

    Edit Other Statistics

    + +
    + +
    + +
    +
    + +
    + + + +
    +
    + + {showModal === 'ConfirmDeleteOtherStatisticsModal' && ( + + Are you sure you want to delete the other statistics? + + )} + + ); + }, +); diff --git a/web-client/src/views/CaseDetail/MessagesInProgress.jsx b/web-client/src/views/CaseDetail/MessagesInProgress.jsx index ddd1e9646cb..e24ec78833e 100644 --- a/web-client/src/views/CaseDetail/MessagesInProgress.jsx +++ b/web-client/src/views/CaseDetail/MessagesInProgress.jsx @@ -38,10 +38,7 @@ export const MessagesInProgress = connect(

    - + {workItem.document.documentTitle || workItem.document.documentType} diff --git a/web-client/src/views/CaseDetail/OtherStatisticsForm.jsx b/web-client/src/views/CaseDetail/OtherStatisticsForm.jsx new file mode 100644 index 00000000000..299d9629748 --- /dev/null +++ b/web-client/src/views/CaseDetail/OtherStatisticsForm.jsx @@ -0,0 +1,57 @@ +import { DollarsInput } from '../../ustc-ui/DollarsInput/DollarsInput'; +import { FormGroup } from '../../ustc-ui/FormGroup/FormGroup'; +import { connect } from '@cerebral/react'; +import { sequences, state } from 'cerebral'; +import React from 'react'; + +export const OtherStatisticsForm = connect( + { + form: state.form, + updateFormValueSequence: sequences.updateFormValueSequence, + }, + function OtherStatisticsForm({ form, updateFormValueSequence }) { + return ( +

    +
    + + + { + updateFormValueSequence({ + key: 'litigationCosts', + value: values.value, + }); + }} + /> + +
    + +
    + + + { + updateFormValueSequence({ + key: 'damages', + value: values.value, + }); + }} + /> + +
    +
    + ); + }, +); diff --git a/web-client/src/views/CaseDetail/Statistics.jsx b/web-client/src/views/CaseDetail/Statistics.jsx new file mode 100644 index 00000000000..2b5e3ba798e --- /dev/null +++ b/web-client/src/views/CaseDetail/Statistics.jsx @@ -0,0 +1,152 @@ +import { Button } from '../../ustc-ui/Button/Button'; +import { connect } from '@cerebral/react'; +import { state } from 'cerebral'; +import React from 'react'; + +export const Statistics = connect( + { + caseDetail: state.caseDetail, + statisticsHelper: state.statisticsHelper, + }, + function Statistics({ caseDetail, statisticsHelper }) { + return ( + <> + {statisticsHelper.showAddButtons && ( +
    + {statisticsHelper.showAddDeficiencyStatisticsButton && ( + + )} + {statisticsHelper.showAddOtherStatisticsButton && ( + + )} +
    + )} + {statisticsHelper.showNoStatistics && ( +

    There are no statistics for this case.

    + )} + {statisticsHelper.formattedStatistics && ( +
    +
    +

    Deficiency

    + + + + + + + + + + {statisticsHelper.formattedStatistics.map( + (statistic, index) => ( + + + + + + ), + )} + +
    Year/PeriodIRS NoticeDetermination
    {statistic.formattedDate}{statistic.formattedIrsDeficiencyAmount} + {statistic.formattedDeterminationDeficiencyAmount} +
    +
    + +
    +

    Penalties

    + + + + + + + + + + {statisticsHelper.formattedStatistics.map( + (statistic, index) => ( + + + + + + ), + )} + +
    IRS NoticeDetermination
    {statistic.formattedIrsTotalPenalties} + {statistic.formattedDeterminationTotalPenalties} + + {statisticsHelper.showEditButtons && ( + + )} +
    +
    +
    + )} + {statisticsHelper.showOtherStatistics && ( +
    +
    +

    Other

    + + + + {statisticsHelper.showLitigationCosts && ( + + )} + {statisticsHelper.showDamages && ( + + )} + + + + + + {statisticsHelper.showLitigationCosts && ( + + )} + {statisticsHelper.showDamages && ( + + )} + + + +
    Litigation costsDamages (IRC §6673)
    {statisticsHelper.formattedLitigationCosts}{statisticsHelper.formattedDamages} + {statisticsHelper.showEditButtons && ( + + )} +
    +
    +
    + )} + + ); + }, +); diff --git a/web-client/src/views/CaseDetailEdit/CaseInfo.jsx b/web-client/src/views/CaseDetailEdit/CaseInfo.jsx index ce61cb48c8e..2626f461d21 100644 --- a/web-client/src/views/CaseDetailEdit/CaseInfo.jsx +++ b/web-client/src/views/CaseDetailEdit/CaseInfo.jsx @@ -76,6 +76,28 @@ export const CaseInfo = connect( )} + {!form.isPaper && ( + <> + + + + {caseDetailEditHelper.receivedAtFormatted} + + + + + + + Electronically filed + + + + )} + - { - updateOrderForDesignatingPlaceOfTrialSequence({ - key: e.target.name, - value: e.target.value || null, - }); - validateCaseDetailSequence(); - }} - /> - -
    - { - updateFormValueSequence({ - key: e.target.name, - value: e.target.checked, - }); - }} - /> - -
    -
    + {form.isPaper && ( + { + updateOrderForDesignatingPlaceOfTrialSequence({ + key: e.target.name, + value: e.target.value || null, + }); + validateCaseDetailSequence(); + }} + /> + )} + + {!form.isPaper && ( + <> + + + + {form.preferredTrialCity} + + + + )} + + {form.isPaper && ( + +
    + { + updateFormValueSequence({ + key: e.target.name, + value: e.target.checked, + }); + }} + /> + +
    +
    + )} - Orders Needed (optional) + Orders/Notices Needed (optional)
    ( {statistic.formattedDate} - {statistic.formattedDeficiencyAmount} - {statistic.formattedTotalPenalties} + + {statistic.formattedIrsDeficiencyAmount} + + + {statistic.formattedIrsTotalPenalties} + ), )} @@ -375,6 +380,7 @@ export const ReviewSavedPetition = connect(
    {showModal == 'ConfirmServeToIrsModal' && } + {showModal == 'FormCancelModalDialog' && ( + + )} ); }, diff --git a/web-client/src/views/CaseInventoryReport/CaseInventoryReport.jsx b/web-client/src/views/CaseInventoryReport/CaseInventoryReport.jsx index 289bf4bbd92..4717915b4ad 100644 --- a/web-client/src/views/CaseInventoryReport/CaseInventoryReport.jsx +++ b/web-client/src/views/CaseInventoryReport/CaseInventoryReport.jsx @@ -140,7 +140,7 @@ export const CaseInventoryReport = connect( )} diff --git a/web-client/src/views/CaseListPetitioner.jsx b/web-client/src/views/CaseListPetitioner.jsx index bef54f3477b..71200626580 100644 --- a/web-client/src/views/CaseListPetitioner.jsx +++ b/web-client/src/views/CaseListPetitioner.jsx @@ -1,66 +1,183 @@ import { Button } from '../ustc-ui/Button/Button'; import { CaseListRowExternal } from './CaseListRowExternal'; +import { Mobile, NonMobile } from '../ustc-ui/Responsive/Responsive'; +import { Tab, Tabs } from '../ustc-ui/Tabs/Tabs'; import { WarningNotification } from './WarningNotification'; import { connect } from '@cerebral/react'; -import { state } from 'cerebral'; +import { sequences, state } from 'cerebral'; import React from 'react'; export const CaseListPetitioner = connect( { - formattedCases: state.formattedCases, + caseType: state.openClosedCases.caseType, + closedTab: state.constants.EXTERNAL_USER_DASHBOARD_TABS.CLOSED, + externalUserCasesHelper: state.externalUserCasesHelper, + openTab: state.constants.EXTERNAL_USER_DASHBOARD_TABS.OPEN, + setCaseTypeToDisplaySequence: sequences.setCaseTypeToDisplaySequence, + showMoreClosedCasesSequence: sequences.showMoreClosedCasesSequence, + showMoreOpenCasesSequence: sequences.showMoreOpenCasesSequence, }, - function CaseListPetitioner({ formattedCases }) { + function CaseListPetitioner({ + caseType, + closedTab, + externalUserCasesHelper, + openTab, + setCaseTypeToDisplaySequence, + showMoreClosedCasesSequence, + showMoreOpenCasesSequence, + }) { + const renderStartButton = () => ( + + ); + + const renderCaseListTable = ({ + cases, + showLoadMore, + showMoreResultsSequence, + tabName, + }) => { + return ( + <> + {!cases?.length &&

    You have no {tabName.toLowerCase()} cases.

    } + {cases.length > 0 && ( + <> + + + + + + + + + + + {cases.map(item => ( + + ))} + +
    + Lead Case Indicator + Docket numberCase titleDate filed
    + {showLoadMore && ( + + )} + + )} + + ); + }; + return ( <> -
    -
    -
    -

    My Cases

    + +
    +
    +
    + + + {renderCaseListTable({ + cases: externalUserCasesHelper.openCaseResults, + showLoadMore: + externalUserCasesHelper.showLoadMoreOpenCases, + showMoreResultsSequence: showMoreOpenCasesSequence, + tabName: openTab, + })} + + + {renderCaseListTable({ + cases: externalUserCasesHelper.closedCaseResults, + showLoadMore: + externalUserCasesHelper.showLoadMoreClosedCases, + showMoreResultsSequence: showMoreClosedCasesSequence, + tabName: closedTab, + })} + +
    + {renderStartButton()} +
    +
    +
    -
    -
    -
    -

    My Cases

    -
    -
    - - - - - - - - - - - {formattedCases.map(item => ( - - ))} - -
    - Lead Case Indicator - Docket numberCase titleDate filed
    -
    + ); }, diff --git a/web-client/src/views/CaseListPractitioner.jsx b/web-client/src/views/CaseListPractitioner.jsx index fa2f22870fd..517ffd77eb3 100644 --- a/web-client/src/views/CaseListPractitioner.jsx +++ b/web-client/src/views/CaseListPractitioner.jsx @@ -1,104 +1,178 @@ import { Button } from '../ustc-ui/Button/Button'; import { CaseListRowExternal } from './CaseListRowExternal'; import { CaseSearchBox } from './CaseSearchBox'; +import { Mobile, NonMobile } from '../ustc-ui/Responsive/Responsive'; import { MyContactInformation } from './MyContactInformation'; +import { Tab, Tabs } from '../ustc-ui/Tabs/Tabs'; import { connect } from '@cerebral/react'; -import { state } from 'cerebral'; +import { sequences, state } from 'cerebral'; import React from 'react'; -import classNames from 'classnames'; export const CaseListPractitioner = connect( { + caseType: state.openClosedCases.caseType, + closedTab: state.constants.EXTERNAL_USER_DASHBOARD_TABS.CLOSED, dashboardExternalHelper: state.dashboardExternalHelper, - formattedCases: state.formattedCases, + externalUserCasesHelper: state.externalUserCasesHelper, + openTab: state.constants.EXTERNAL_USER_DASHBOARD_TABS.OPEN, + setCaseTypeToDisplaySequence: sequences.setCaseTypeToDisplaySequence, + showMoreClosedCasesSequence: sequences.showMoreClosedCasesSequence, + showMoreOpenCasesSequence: sequences.showMoreOpenCasesSequence, }, - function CaseListPractitioner({ dashboardExternalHelper, formattedCases }) { - const renderTable = () => ( -
    - - - - - - - - - - - {formattedCases.map(item => ( - - ))} - -
    - Lead Case Indicator - Docket numberCase titleDate filed
    -
    + function CaseListPractitioner({ + caseType, + closedTab, + dashboardExternalHelper, + externalUserCasesHelper, + openTab, + setCaseTypeToDisplaySequence, + showMoreClosedCasesSequence, + showMoreOpenCasesSequence, + }) { + const renderTable = ( + cases, + showLoadMore, + showMoreResultsSequence, + tabName, + ) => ( + <> + {!cases?.length &&

    You have no {tabName.toLowerCase()} cases.

    } + {cases.length > 0 && ( + + + + + + + + + + + {cases.map(item => ( + + ))} + +
    + Lead Case Indicator + Docket numberCase titleDate filed
    + )} + {showLoadMore && ( + + )} + ); - const renderTitle = () =>

    My Cases

    ; - const renderStartButton = () => ( ); - const renderEmptyState = () => ( - - {renderTitle()} -

    - You are not associated with any cases. -

    - {renderStartButton()} -
    - ); - - const renderNonEmptyState = () => ( - -
    -
    -
    -

    My Cases

    -
    -
    - {renderStartButton()} -
    -
    -
    -
    -

    My Cases

    -
    - {renderTable()} -
    - ); - return ( <> -
    -
    -
    - {dashboardExternalHelper.showCaseList - ? renderNonEmptyState() - : renderEmptyState()} + +
    +
    +
    + + + {renderTable( + externalUserCasesHelper.openCaseResults, + externalUserCasesHelper.showLoadMoreOpenCases, + showMoreOpenCasesSequence, + openTab, + )} + + + {renderTable( + externalUserCasesHelper.closedCaseResults, + externalUserCasesHelper.showLoadMoreClosedCases, + showMoreClosedCasesSequence, + closedTab, + )} + +
    + {renderStartButton()} +
    +
    +
    +
    + {dashboardExternalHelper.showCaseSearch && } + +
    +
    +
    +
    + +
    +
    {renderStartButton()}
    +
    +
    -
    - {dashboardExternalHelper.showCaseSearch && } - +
    + {caseType === closedTab && + renderTable( + externalUserCasesHelper.closedCaseResults, + externalUserCasesHelper.showLoadMoreClosedCases, + showMoreClosedCasesSequence, + closedTab, + )} + {caseType === openTab && + renderTable( + externalUserCasesHelper.openCaseResults, + externalUserCasesHelper.showLoadMoreOpenCases, + showMoreOpenCasesSequence, + openTab, + )}
    -
    +
    ); }, diff --git a/web-client/src/views/CaseListRespondent.jsx b/web-client/src/views/CaseListRespondent.jsx index d96668e4ed1..618536260cd 100644 --- a/web-client/src/views/CaseListRespondent.jsx +++ b/web-client/src/views/CaseListRespondent.jsx @@ -1,82 +1,168 @@ +import { Button } from '../ustc-ui/Button/Button'; import { CaseListRowExternal } from './CaseListRowExternal'; import { CaseSearchBox } from './CaseSearchBox'; +import { Mobile, NonMobile } from '../ustc-ui/Responsive/Responsive'; import { MyContactInformation } from './MyContactInformation'; +import { Tab, Tabs } from '../ustc-ui/Tabs/Tabs'; import { connect } from '@cerebral/react'; -import { state } from 'cerebral'; +import { sequences, state } from 'cerebral'; import React from 'react'; export const CaseListRespondent = connect( { + caseType: state.openClosedCases.caseType, + closedTab: state.constants.EXTERNAL_USER_DASHBOARD_TABS.CLOSED, dashboardExternalHelper: state.dashboardExternalHelper, - formattedCases: state.formattedCases, + externalUserCasesHelper: state.externalUserCasesHelper, + openTab: state.constants.EXTERNAL_USER_DASHBOARD_TABS.OPEN, + pageSize: state.constants.CASE_LIST_PAGE_SIZE, + setCaseTypeToDisplaySequence: sequences.setCaseTypeToDisplaySequence, + showMoreClosedCasesSequence: sequences.showMoreClosedCasesSequence, + showMoreOpenCasesSequence: sequences.showMoreOpenCasesSequence, }, - function CaseListRespondent({ dashboardExternalHelper, formattedCases }) { - const renderTable = () => ( -
    - - - - - - - - - - - {formattedCases.map(item => ( - - ))} - -
    - Lead Case Indicator - Docket numberCase titleDate filed
    -
    - ); - - const renderTitle = () =>

    My Cases

    ; - - const renderEmptyState = () => ( + function CaseListRespondent({ + caseType, + closedTab, + dashboardExternalHelper, + externalUserCasesHelper, + openTab, + setCaseTypeToDisplaySequence, + showMoreClosedCasesSequence, + showMoreOpenCasesSequence, + }) { + const renderTable = ( + cases, + showLoadMore, + showMoreResultsSequence, + tabName, + ) => ( <> - {renderTitle()} -

    You are not associated with any cases.

    + {!cases?.length &&

    You have no {tabName.toLowerCase()} cases.

    } + {cases.length > 0 && ( + + + + + + + + + + + {cases.map(item => ( + + ))} + +
    + Lead Case Indicator + Docket numberCase titleDate filed
    + )} + {showLoadMore && ( + + )} ); - const renderNonEmptyState = () => ( + return ( <> -
    -
    -
    -

    My Cases

    + +
    +
    +
    + + + {renderTable( + externalUserCasesHelper.openCaseResults, + externalUserCasesHelper.showLoadMoreOpenCases, + showMoreOpenCasesSequence, + openTab, + )} + + + {renderTable( + externalUserCasesHelper.closedCaseResults, + externalUserCasesHelper.showLoadMoreClosedCases, + showMoreClosedCasesSequence, + closedTab, + )} + + +
    +
    + {dashboardExternalHelper.showCaseSearch && } + +
    -
    -
    -

    My Cases

    -
    - {renderTable()} - - ); - - return ( - <> -
    -
    -
    - {dashboardExternalHelper.showCaseList - ? renderNonEmptyState() - : renderEmptyState()} + + +
    +
    + +
    +
    + {caseType === closedTab && + renderTable( + externalUserCasesHelper.closedCaseResults, + externalUserCasesHelper.showLoadMoreClosedCases, + showMoreClosedCasesSequence, + closedTab, + )} + {caseType === openTab && + renderTable( + externalUserCasesHelper.openCaseResults, + externalUserCasesHelper.showLoadMoreOpenCases, + showMoreOpenCasesSequence, + openTab, + )}
    -
    - +
    + {dashboardExternalHelper.showCaseSearch && }
    -
    +
    ); }, diff --git a/web-client/src/views/Correspondence/Correspondence.jsx b/web-client/src/views/Correspondence/Correspondence.jsx index 4bf936d5974..675a9d76452 100644 --- a/web-client/src/views/Correspondence/Correspondence.jsx +++ b/web-client/src/views/Correspondence/Correspondence.jsx @@ -7,66 +7,100 @@ import React from 'react'; export const Correspondence = connect( { + baseUrl: state.baseUrl, formattedCaseDetail: state.formattedCaseDetail, openConfirmDeleteCorrespondenceModalSequence: sequences.openConfirmDeleteCorrespondenceModalSequence, + showAddCorrespondenceButton: + state.caseDetailHelper.showAddCorrespondenceButton, showModal: state.modal.showModal, + token: state.token, }, function Correspondence({ + baseUrl, + formattedCaseDetail, openConfirmDeleteCorrespondenceModalSequence, + showAddCorrespondenceButton, showModal, + token, }) { return ( <> - - - - - - - - - - - - - - - - - -
    DateCorrespondence DescriptionCreated By
    - 01/01/2001 - - - Name of the user who filed this - - - -
    + {formattedCaseDetail.correspondence.length === 0 && ( +

    There are no correspondence files.

    + )} + {formattedCaseDetail.correspondence.length > 0 && ( + + + + + + + + + + {formattedCaseDetail.correspondence.map((document, index) => { + return ( + + + + + + + + ); + })} + +
    DateCorrespondence DescriptionCreated By
    + + {document.formattedFilingDate} + + + + {document.filedBy} + {showAddCorrespondenceButton && ( + + )} + + {showAddCorrespondenceButton && ( + + )} +
    + )} {showModal === 'DeleteCorrespondenceModal' && ( diff --git a/web-client/src/views/Correspondence/CorrespondenceHeader.jsx b/web-client/src/views/Correspondence/CorrespondenceHeader.jsx index 63f06ce6f5c..f8988a4a9e3 100644 --- a/web-client/src/views/Correspondence/CorrespondenceHeader.jsx +++ b/web-client/src/views/Correspondence/CorrespondenceHeader.jsx @@ -1,30 +1,18 @@ import { Button } from '../../ustc-ui/Button/Button'; import { connect } from '@cerebral/react'; -import { sequences, state } from 'cerebral'; +import { state } from 'cerebral'; import React from 'react'; export const CorrespondenceHeader = connect( { formattedCaseDetail: state.formattedCaseDetail, - navigateToPrintableDocketRecordSequence: - sequences.navigateToPrintableDocketRecordSequence, - printDocketRecordSequence: sequences.printDocketRecordSequence, - toggleMobileDocketSortSequence: sequences.toggleMobileDocketSortSequence, - updateSessionMetadataSequence: sequences.updateSessionMetadataSequence, + showAddCorrespondenceButton: + state.caseDetailHelper.showAddCorrespondenceButton, }, function CorrespondenceHeader({ formattedCaseDetail, - navigateToPrintableDocketRecordSequence, - printDocketRecordSequence, - updateSessionMetadataSequence, + showAddCorrespondenceButton, }) { - const openDocketRecordPrintPreview = (options = {}) => { - updateSessionMetadataSequence({ - key: `docketRecordSort.${formattedCaseDetail.caseId}`, - value: 'byDate', - }); - printDocketRecordSequence(options); - }; return (
    @@ -33,35 +21,31 @@ export const CorrespondenceHeader = connect(

    Correspondence Files

    - -
    -
    - + {showAddCorrespondenceButton && ( + + )}
    + {showAddCorrespondenceButton && ( +
    + +
    + )}
    diff --git a/web-client/src/views/Correspondence/DeleteCorrespondenceModal.jsx b/web-client/src/views/Correspondence/DeleteCorrespondenceModal.jsx index cd9f07aaeea..2d67c6a4c81 100644 --- a/web-client/src/views/Correspondence/DeleteCorrespondenceModal.jsx +++ b/web-client/src/views/Correspondence/DeleteCorrespondenceModal.jsx @@ -1,24 +1,26 @@ import { ConfirmModal } from '../../ustc-ui/Modal/ConfirmModal'; import { connect } from '@cerebral/react'; -import { sequences } from 'cerebral'; +import { state } from 'cerebral'; import React from 'react'; export const DeleteCorrespondenceModal = connect( - { clearModalSequence: sequences.clearModalSequence }, - function DeleteCorrespondenceModal() { + { + correspondenceTitle: state.modal.correspondenceToDelete.documentTitle, + }, + function DeleteCorrespondenceModal({ correspondenceTitle }) { return (
    Once deleted, it can’t be restored.
    - Internal Memo + {correspondenceTitle}
    ); diff --git a/web-client/src/views/Correspondence/EditCorrespondenceDocument.jsx b/web-client/src/views/Correspondence/EditCorrespondenceDocument.jsx new file mode 100644 index 00000000000..632389d5d82 --- /dev/null +++ b/web-client/src/views/Correspondence/EditCorrespondenceDocument.jsx @@ -0,0 +1,210 @@ +import { Button } from '../../ustc-ui/Button/Button'; +import { CaseDetailHeader } from '../CaseDetail/CaseDetailHeader'; +import { DocumentDisplayIframe } from '../DocumentDetail/DocumentDisplayIframe'; +import { ErrorNotification } from '../ErrorNotification'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { FormCancelModalDialog } from '../FormCancelModalDialog'; +import { FormGroup } from '../../ustc-ui/FormGroup/FormGroup'; +import { Hint } from '../../ustc-ui/Hint/Hint'; +import { StateDrivenFileInput } from '../FileDocument/StateDrivenFileInput'; +import { SuccessNotification } from '../SuccessNotification'; +import { connect } from '@cerebral/react'; +import { sequences, state } from 'cerebral'; +import React from 'react'; +import classNames from 'classnames'; + +export const EditCorrespondenceDocument = connect( + { + cancelAndNavigateToCorrespondenceSequence: + sequences.cancelAndNavigateToCorrespondenceSequence, + clearExistingDocumentSequence: sequences.clearExistingDocumentSequence, + constants: state.constants, + documentDetailHelper: state.documentDetailHelper, + editCorrespondenceDocumentSequence: + sequences.editCorrespondenceDocumentSequence, + fileDocumentHelper: state.fileDocumentHelper, + form: state.form, + formCancelToggleCancelSequence: sequences.formCancelToggleCancelSequence, + screenMetadata: state.screenMetadata, + showModal: state.modal.showModal, + updateFormValueSequence: sequences.updateFormValueSequence, + validateUploadCorrespondenceDocumentSequence: + sequences.validateUploadCorrespondenceDocumentSequence, + validationErrors: state.validationErrors, + }, + function EditCorrespondenceDocument({ + clearExistingDocumentSequence, + constants, + documentDetailHelper, + editCorrespondenceDocumentSequence, + fileDocumentHelper, + form, + formCancelToggleCancelSequence, + screenMetadata, + showModal, + updateFormValueSequence, + validateUploadCorrespondenceDocumentSequence, + validationErrors, + }) { + return ( + <> + + {showModal === 'FormCancelModalDialog' && ( + + )} + +
    + + + {screenMetadata.documentReset && ( + When you submit it will overwrite the previous document + )} +
    +
    +
    +

    + Edit {documentDetailHelper.formattedDocument.documentTitle} +

    +
    +
    + +
    +
    +
    + + + { + updateFormValueSequence({ + key: e.target.name, + value: e.target.value, + }); + validateUploadCorrespondenceDocumentSequence(); + }} + /> + +
    +
    + +
    + {(screenMetadata.documentReset && ( + <> +
    +
    +
    +
    +

    + Add PDF +

    +
    +
    +
    +
    + +
    + + + + File must be in PDF format (.pdf). Max file size{' '} + {constants.MAX_FILE_SIZE_MB}MB. + + + + +
    + + )) || ( + <> +
    +
    +
    +
    +

    + View PDF + +

    +
    +
    +
    +
    + + + )} +
    +
    + +
    +
    + + +
    +
    +
    +
    + + ); + }, +); diff --git a/web-client/src/views/Correspondence/UploadCorrespondenceDocument.jsx b/web-client/src/views/Correspondence/UploadCorrespondenceDocument.jsx index 77379831135..317461f7685 100644 --- a/web-client/src/views/Correspondence/UploadCorrespondenceDocument.jsx +++ b/web-client/src/views/Correspondence/UploadCorrespondenceDocument.jsx @@ -37,7 +37,8 @@ export const UploadCorrespondenceDocument = connect( }) { return ( <> - + + {showModal === 'FormCancelModalDialog' && ( )} @@ -56,7 +57,9 @@ export const UploadCorrespondenceDocument = connect(