diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7abaf96340..56f00fd727 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,30 @@ -## Purpose + -_Describe the problem or feature in addition to a link to the issues._ +## 🎫 Linked Ticket -#### Linked Issues to Close + -_Links to issue(s) that are closed by this PR. Be sure to use the phrase "Closes #XXX" for each issue, so they automatically close_ +[Ticket to close](link-to-ticket) -## Approach +## 💬 Description / Notes -_How does this change address the issue?_ + + -## Assorted Notes/Considerations/Learning +## 🛠 Changes -_List any other information that you think is important... a post-merge activity, someone to notify, what you learned, etc._ + + +## 📸 Screenshots / Demo + + + + diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9139320b1f..5d0925e70f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -130,7 +130,6 @@ jobs: coverageLocations: coverage/lcov.info:lcov e2e: - timeout-minutes: 5 runs-on: ubuntu-20.04 needs: - deploy @@ -148,8 +147,6 @@ jobs: role-to-assume: ${{ secrets.AWS_OIDC_ROLE_TO_ASSUME }} aws-region: us-east-1 role-duration-seconds: 10800 - - name: Install Playwright with Chrome - run: npx playwright install --with-deps chromium - name: Run e2e tests run: run e2e - uses: actions/upload-artifact@v3 diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml deleted file mode 100644 index 1adee7bcf0..0000000000 --- a/.github/workflows/github-pages.yml +++ /dev/null @@ -1,87 +0,0 @@ -# Sample workflow for building and deploying a Jekyll site to GitHub Pages -name: GitHub Pages - -on: - workflow_run: - workflows: - - Deploy - types: - - completed - branches: - - main - - val - - production - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - repository-projects: write - actions: write - pull-requests: write - -# Allow one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: true - -env: - STAGE_NAME: ${{ startsWith(github.ref_name, 'snyk-') && 'snyk' || github.ref_name }} - -jobs: - # Build job - build: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: main - - name: Setup Pages - uses: actions/configure-pages@v2 - - uses: ./.github/actions/setup - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_OIDC_ROLE_TO_ASSUME }} - aws-region: us-east-1 - role-duration-seconds: 10800 - - name: Install Packages - run: | - cd docs/_deploy-metrics - rm -rf node_modules - bun install - echo $BRANCHES_TO_GENERATE - - name: Build Deploy Metrics - run: | - cd docs/_deploy-metrics - bun run build - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - REPO_NAME: OneMAC - BRANCHES_TO_GENERATE: main,val - - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1 - with: - source: ./docs - destination: _site - - name: Upload artifact - uses: actions/upload-pages-artifact@v1 - - # Deployment job - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 diff --git a/bin/cli/src/commands/e2e.ts b/bin/cli/src/commands/e2e.ts index 8d06be5d48..f24c6bb8d1 100644 --- a/bin/cli/src/commands/e2e.ts +++ b/bin/cli/src/commands/e2e.ts @@ -12,8 +12,9 @@ export const e2e = { }), handler: async ({ ui }: { ui: boolean }) => { await checkIfAuthenticated(); - await runCommand("bun", ["playwright", "install", "--with-deps"], "."); + console.log(ui); + await runCommand("bun", ["playwright", "install", "--with-deps", "chromium"], "."); - await runCommand("bun", [ui ? "e2e:ui" : "e2e"], "."); + await runCommand("bun", [ui ? "e2e:ui" : "e2e", "--", "--", "--grep", "@CI"], "."); }, }; diff --git a/bin/cli/src/commands/logs.ts b/bin/cli/src/commands/logs.ts index 77df7b35e5..91009385f0 100644 --- a/bin/cli/src/commands/logs.ts +++ b/bin/cli/src/commands/logs.ts @@ -127,7 +127,7 @@ async function getLambdasWithTags(tags: Tag[]): Promise { const functionData = await lambdaClient.send(functionCommand); return functionData.Configuration?.FunctionName || ""; } catch { - console.log(`Ecluding function ${arn}.`); + console.log(`Excluding function ${arn}.`); return ""; } }), diff --git a/bun.lockb b/bun.lockb old mode 100755 new mode 100644 index 4d043dc0cf..49d1a67202 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docs/.dockerignore b/docs/.dockerignore deleted file mode 100644 index 68feb7d26c..0000000000 --- a/docs/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -Gemfile.lock \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 79565a7842..0000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.gem -.bundle -.ruby-version -.jekyll-cache -.sass-cache -_site -Gemfile.lock -node_modules -.jekyll-metadata -metrics -**/*.DS_Store \ No newline at end of file diff --git a/docs/Gemfile b/docs/Gemfile deleted file mode 100644 index af39f3fe7c..0000000000 --- a/docs/Gemfile +++ /dev/null @@ -1,8 +0,0 @@ -source "https://rubygems.org" - -gem "jekyll" -gem "jekyll-remote-theme" -gem "jekyll-include-cache" -gem "jekyll-seo-tag" -gem "rake" -gem "webrick" diff --git a/docs/_config.yml b/docs/_config.yml deleted file mode 100644 index 6d1609fb7f..0000000000 --- a/docs/_config.yml +++ /dev/null @@ -1,163 +0,0 @@ -# Welcome to Jekyll! -# -# This is the main configuration file for the documentation site, powered by Jekyll. -# You can read about Jekyll configuration options here: https://jekyllrb.com/docs/configuration/ -# The variables set in this file are used by Jekyll, and are also referenced by documentation pages -# throughout the site with the syntax {{ site.my_variable_path_from_config_yml }} -# ex: "This is the {{ site.title }}" -> "This is the Base Template" -# That's the short story. Please update this file for your project. -# If running Jekyll locally, changes to this file require a full restart of Jekyll. -# Thanks, and have fun! -# - MACPro Platform Team - -title: MAKO -description: A new project by the CMS MACPRO platform team. -# baseurl: "/just-the-docs" # the subpath of your site, e.g. /blog -url: "https://enterprise-cmcs.github.io/" # the base hostname & protocol for your site, e.g. http://example.com -keep_files: ["docs/metrics/_next"] -include: ["_next", "_app-*.js", "_buildManifest.js", "_ssgManifest.js"] -exclude: - [ - ".jekyll-cache", - "node_modules/", - "*.gemspec", - "*.gem", - "Gemfile", - "Gemfile.lock", - "package.json", - "package-lock.json", - "script/", - "LICENSE.txt", - "lib/", - "bin/", - "docs/metrics/_next", - "README.md", - "Rakefile", - ] - -contact_email: bpaige@gswell.com - -team: - members: # This list automatically populates the Team Introduction page. Add/Edit as appropriate. - - role: Product - description: Responsible for project scope, direction, and delivery. - name: Hannah Morris - email: hmorris@gswell.com - - role: Product - description: Responsible for project scope, direction, and delivery. - name: Erika Durant - email: edurant@fearless.tech - - role: Tech Lead - description: Tooling, tech, and arch discussions and decisions. - name: Ben Paige - email: bpaige@gswell.com - - role: Tech Lead - description: Tooling, tech, and arch discussions and decisions. - name: Michael Dial - email: mdial@gswell.com - core_hours: 10:00am - 3:00pm ET - -meetings: - - name: Standup - days: Daily - time: 11:30am - 12:00pm ET - link: Bookmarked in Slack Channel. - - name: Touchpoint - days: Wed - time: 10:00am - 10:30am ET - link: Ask for the Hangouts invite on Slack. - -repo: - org: Enterprise-CMCS - name: macpro-mako - url: https://github.com/Enterprise-CMCS/macpro-mako - -slack: - channel_url: https://cmsgov.slack.com/archives/C05ECGY0F5F - -remote_theme: just-the-docs/just-the-docs -permalink: pretty -exclude: [".jekyll-cache", "node_modules/", "*.gemspec", "*.gem", "Gemfile", "Gemfile.lock", "package.json", "package-lock.json", "script/", "LICENSE.txt", "lib/", "bin/", "README.md", "Rakefile"] - -layout: default -nav_order: 2 -has_children: true -has_toc: false - -# Default search settings. -search_enabled: true -search: - heading_level: 2 - previews: 2 - preview_words_before: 3 - preview_words_after: 3 - tokenizer_separator: /[\s/]+/ - rel_url: true - button: false - -heading_anchors: true - -# Aux links for the upper right navigation -aux_links: - "GitHub": - - "https://github.com/Enterprise-CMCS/macpro-mako" - "Jira": - - "https://qmacbis.atlassian.net/jira/software/c/projects/OY2/boards/257" - "Slack": - - "https://cmsgov.slack.com/archives/C05ECGY0F5F" -aux_links_new_tab: false - -# Sort order for navigation links -nav_sort: case_sensitive # Capital letters sorted before lowercase - -# External navigation links -nav_external_links: - - title: GitHub - url: https://github.com/Enterprise-CMCS/macpro-mako - - title: Jira - url: https://qmacbis.atlassian.net/jira/software/c/projects/OY2/boards/257 - - title: Slack - url: https://cmsgov.slack.com/archives/C05ECGY0F5F - -# Footer content -# appears at the bottom of every page's main content -back_to_top: true -back_to_top_text: "Back to top" -footer_content: "A work of the MACPRO Platform Team for the Centers for Medicare & Medicaid Services." -last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter -last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html - -# Footer "Edit this page on GitHub" link text -gh_edit_link: true # show or hide edit this page link -gh_edit_link_text: "Edit this page on GitHub" -gh_edit_repository: "https://github.com/Enterprise-CMCS/macpro-mako" # the github URL for your repo -gh_edit_branch: "main" # the branch that your docs is served from -gh_edit_source: docs # the source that your files originate from -gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately - -# Color scheme currently only supports "dark", "light"/nil (default), or a custom scheme that you define -color_scheme: nil - -# # Google Analytics Tracking (optional) -# # e.g, UA-1234567-89 -# ga_tracking: UA-2709176-10 -# ga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true/nil by default) - -plugins: - - jekyll-remote-theme - - jekyll-seo-tag - -kramdown: - syntax_highlighter_opts: - block: - line_numbers: false - -compress_html: - clippings: all - comments: all - endings: all - startings: [] - blanklines: false - profile: false - # ignore: - # envs: all diff --git a/docs/_deploy-metrics/.eslintrc.json b/docs/_deploy-metrics/.eslintrc.json deleted file mode 100644 index bffb357a71..0000000000 --- a/docs/_deploy-metrics/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/docs/_deploy-metrics/.gitignore b/docs/_deploy-metrics/.gitignore deleted file mode 100644 index 30283ce1dd..0000000000 --- a/docs/_deploy-metrics/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store - -# debug -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local -.env - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/docs/_deploy-metrics/bun.lockb b/docs/_deploy-metrics/bun.lockb deleted file mode 100755 index 67cf56a92f..0000000000 Binary files a/docs/_deploy-metrics/bun.lockb and /dev/null differ diff --git a/docs/_deploy-metrics/components/Card.tsx b/docs/_deploy-metrics/components/Card.tsx deleted file mode 100644 index e8609fbda3..0000000000 --- a/docs/_deploy-metrics/components/Card.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Box, Spacer, Text } from "@chakra-ui/react"; -import React from "react"; - -export interface CardProps { - questionText: string; - questionAnswer: string; -} - -export const Card = ({ questionAnswer, questionText }: CardProps) => { - return ( - - {questionText} - - - {questionAnswer} - - - ); -}; diff --git a/docs/_deploy-metrics/components/Cards.tsx b/docs/_deploy-metrics/components/Cards.tsx deleted file mode 100644 index cb44bb508c..0000000000 --- a/docs/_deploy-metrics/components/Cards.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Table, Td, Text, Th, Thead, Tbody, Tr } from "@chakra-ui/react"; -import { CardProps } from "./Card"; - -interface CardsProps { - cards: CardProps[]; -} - -export const Cards = ({ cards }: CardsProps) => { - return ( - - - - - - - - - {cards.map((card, index) => ( - - - - - ))} - -
Metric TrackedResult
- {card.questionText} - - {card.questionAnswer} -
- ); -}; diff --git a/docs/_deploy-metrics/components/CheckboxFilter.tsx b/docs/_deploy-metrics/components/CheckboxFilter.tsx deleted file mode 100644 index a61c119816..0000000000 --- a/docs/_deploy-metrics/components/CheckboxFilter.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import * as UI from "@chakra-ui/react"; - -type CheckboxFilterProps = Omit & { - options: Array<{ label: string; value: string; count?: number }>; - label: string; - onChange?: (value: string[]) => void; - spacing?: UI.StackProps["spacing"]; - showSearch?: boolean; -}; - -export const CheckboxFilter = (props: CheckboxFilterProps) => { - const { options, label, spacing = "2", ...rest } = props; - - return ( - - - {label} - - - {options.map((option) => ( - - {option.label} - {option.count != null && ( - - {" "} - ({option.count}) - - )} - - ))} - - - ); -}; diff --git a/docs/_deploy-metrics/components/CheckboxFilterPopover.tsx b/docs/_deploy-metrics/components/CheckboxFilterPopover.tsx deleted file mode 100644 index a377db90ef..0000000000 --- a/docs/_deploy-metrics/components/CheckboxFilterPopover.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useFilterState } from "../hooks/useFilterState"; -import { CheckboxFilter } from "./CheckboxFilter"; -import { FilterPopoverButton, FilterPopoverContent } from "./FilterPopover"; -import * as UI from "@chakra-ui/react"; - -export interface FilterOption { - label: string; - value: string; - count: number; -} - -interface CheckboxFilterPopoverProps { - label: string; - onSubmit: ((value: never[]) => void) | undefined; - options: FilterOption[]; - filtersApplied: number; -} - -export const CheckboxFilterPopover = ({ - label, - onSubmit, - options, - filtersApplied, -}: CheckboxFilterPopoverProps) => { - const state = useFilterState({ - defaultValue: [], - onSubmit, - }); - - return ( - - - filtersApplied ? ( - - {filtersApplied} - - ) : null - } - /> - - state.onChange(v as any)} - options={options} - /> - - - ); -}; diff --git a/docs/_deploy-metrics/components/DeploymentFrequency.tsx b/docs/_deploy-metrics/components/DeploymentFrequency.tsx deleted file mode 100644 index 1b4d040d3d..0000000000 --- a/docs/_deploy-metrics/components/DeploymentFrequency.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Container, Heading, Text } from "@chakra-ui/react"; -import { - Cell, - Legend, - Pie, - PieChart, - ResponsiveContainer, - Tooltip, -} from "recharts"; -import { capitalizeFirstLetter } from "../lib/capitalizeLetter"; -import type { getSuccessfulDeploys } from "../lib/getSuccessfulDeploys"; - -type Props = { - successfulDeploys: Awaited>; - selectedBranch: string; -}; - -export const DeploymentFrequency = ({ - selectedBranch, - successfulDeploys, -}: Props) => { - const pieChartData = [ - { name: "passed", value: successfulDeploys.passedRuns }, - { name: "failed", value: successfulDeploys.failedRuns }, - ]; - - const colors = ["#0088FE", "#FF8042"]; - - return ( - - - {capitalizeFirstLetter(selectedBranch)} Deployments - - - The amount of successful deploys compared to unsuccessful deploys. - - - - - - {pieChartData.map((_entry, index) => ( - - ))} - - - - - - ); -}; diff --git a/docs/_deploy-metrics/components/FilterPopover.tsx b/docs/_deploy-metrics/components/FilterPopover.tsx deleted file mode 100644 index 8f35bde28b..0000000000 --- a/docs/_deploy-metrics/components/FilterPopover.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import * as UI from "@chakra-ui/react"; -import { ElementType, ReactNode } from "react"; -import { HiChevronDown } from "react-icons/hi"; - -export type FilterActionButtonsProps = { - onClickCancel?: VoidFunction; - isCancelDisabled?: boolean; - onClickApply?: VoidFunction; -}; - -export const FilterActionButtons = (props: FilterActionButtonsProps) => { - const { onClickApply, onClickCancel, isCancelDisabled } = props; - return ( - - - Cancel - - - Save - - - ); -}; - -type FilterPopoverButtonProps = { - label: string; - icon?: ElementType; - selected?: boolean; -}; - -export const FilterPopoverButton = (props: FilterPopoverButtonProps) => { - const { label, icon, selected } = props; - - return ( - - - {icon && } - {label} - - - - ); -}; - -type FilterPopoverContentProps = FilterActionButtonsProps & { - header?: string; - children?: ReactNode; -}; - -export const FilterPopoverContent = (props: FilterPopoverContentProps) => { - const { children, onClickCancel, onClickApply, isCancelDisabled } = props; - const { onClose } = UI.usePopoverContext(); - return ( - - {children} - - { - onClickCancel?.(); - onClose(); - }} - isCancelDisabled={isCancelDisabled} - onClickApply={() => { - onClickApply?.(); - onClose(); - }} - /> - - - ); -}; diff --git a/docs/_deploy-metrics/components/LeadTimeForChanges.tsx b/docs/_deploy-metrics/components/LeadTimeForChanges.tsx deleted file mode 100644 index 2a3ff8fd38..0000000000 --- a/docs/_deploy-metrics/components/LeadTimeForChanges.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { getPrsToBranch } from "../lib/getPrsToBranch"; -import { - CartesianGrid, - Legend, - Line, - LineChart, - ReferenceLine, - ResponsiveContainer, - Tooltip, - YAxis, -} from "recharts"; -import { Heading, Text } from "@chakra-ui/react"; - -export const LeadTimeForChanges = ( - props: Awaited> -) => { - return ( - <> - - Time to close PRs (Pull-Requests) - - - The red line in the below chart is an indicator of average time. - - - - - - - - - - - - - ); -}; diff --git a/docs/_deploy-metrics/components/ResourceTable.tsx b/docs/_deploy-metrics/components/ResourceTable.tsx deleted file mode 100644 index 985dafb4fa..0000000000 --- a/docs/_deploy-metrics/components/ResourceTable.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* eslint-disable */ - -import * as UI from "@chakra-ui/react"; -import { formatDistance } from "date-fns"; -import { Resource } from "../lib/getAwsResources"; - -const ResourceTypeLabel = ({ type }: { type: string }) => { - const iconName = type.split("::")[1]; - try { - var ICON = require(`react-aws-icons/dist/aws/logo/${iconName}`).default; - } catch { - var ICON = require(`react-aws-icons/dist/aws/logo/AWS`).default; - } - - return ( - - - - {type} - - - ); -}; - -export const ResourceTable = ({ data }: { data: Resource[] }) => { - return ( - - - - - - - Type - - - - Status - ID - Last Updated - Stack - - - - {data.map((d) => { - return ( - - - - - - - - - {d.ResourceStatus.includes("FAILED") ? "failed" : "healthy"} - - - - - {d.LogicalResourceId.length > 45 - ? `${d.LogicalResourceId.slice(0, 45)}...` - : d.LogicalResourceId} - - - - - {formatDistance(new Date(d.LastUpdatedTimestamp), new Date())}{" "} - ago - - - - {d.StackName} - - - ); - })} - - - ); -}; diff --git a/docs/_deploy-metrics/components/Resources.tsx b/docs/_deploy-metrics/components/Resources.tsx deleted file mode 100644 index f35f8475d8..0000000000 --- a/docs/_deploy-metrics/components/Resources.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import * as UI from "@chakra-ui/react"; -import { useState } from "react"; -import { Resource } from "../lib/getAwsResources"; -import { getStackOptions, getTypeOptions } from "../lib/getFilterOptions"; -import { CheckboxFilterPopover } from "./CheckboxFilterPopover"; -import { ResourceTable } from "./ResourceTable"; -import CsvDownloadButton from "react-json-to-csv"; - -export const Resources = ({ - data, - downloadFileName, -}: { - data: Resource[]; - downloadFileName: string; -}) => { - const [typeFilter, setTypeFilter] = useState<{ - options: string[]; - }>({ options: [] }); - const [stackFilter, setStackFilter] = useState<{ - options: string[]; - }>({ options: [] }); - let filteredData = [...data]; - - if (stackFilter.options.length > 0) { - filteredData = data.filter((item) => - stackFilter.options.includes(item.StackName) - ); - } - if (typeFilter.options.length > 0) { - filteredData = filteredData.filter((obj) => { - for (const type of typeFilter.options) { - if (obj.ResourceType.includes(type)) { - return true; - } - } - return false; - }); - } - - return ( - - - - - - - Resources - - - 0 ? filteredData : data - )} - onSubmit={(options) => setTypeFilter({ options })} - /> - 0 ? filteredData : data - )} - onSubmit={(options) => setStackFilter({ options })} - /> - - Download Data - - - - - - - - - - - ); -}; diff --git a/docs/_deploy-metrics/hooks/useFilterState.tsx b/docs/_deploy-metrics/hooks/useFilterState.tsx deleted file mode 100644 index da28538b6e..0000000000 --- a/docs/_deploy-metrics/hooks/useFilterState.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useState } from "react"; - -export type UseFilterStateProps = { - defaultValue: T | undefined; - onSubmit?: (value: T) => void; -}; - -export function useFilterState(props: UseFilterStateProps) { - const { defaultValue, onSubmit } = props; - const [state, setState] = useState(defaultValue); - return { - canCancel: defaultValue !== state, - value: state, - onChange: setState, - onReset() { - setState(defaultValue); - }, - onSubmit() { - onSubmit?.(state as T); - }, - }; -} diff --git a/docs/_deploy-metrics/lib/capitalizeLetter.ts b/docs/_deploy-metrics/lib/capitalizeLetter.ts deleted file mode 100644 index b9f7fb9152..0000000000 --- a/docs/_deploy-metrics/lib/capitalizeLetter.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const capitalizeFirstLetter = (string: string) => { - return string.charAt(0).toUpperCase() + string.slice(1); -}; diff --git a/docs/_deploy-metrics/lib/formData.ts b/docs/_deploy-metrics/lib/formData.ts deleted file mode 100644 index 04bc972a2b..0000000000 --- a/docs/_deploy-metrics/lib/formData.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* eslint no-prototype-builtins: 0 */ // --> OFF - -export type FormResult = { - version: string; - data: any; // replace 'any' with the actual type of the data returned from the API - } | null - - export type ResultObject = { - [formId: string]: FormResult[]; - } - - export async function getAllFormData(formData: any): Promise { - const resultObject: ResultObject = {}; - - // Loop through each key-value pair in formData - for (const formId in formData) { - if (formData.hasOwnProperty(formId)) { - const formVersions = formData[formId]; - - // Loop through each formVersion for the current formId - resultObject[formId] = await Promise.all( - formVersions.map(async (formVersion: any) => { - try { - // Make API request using fetch - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_REST_URL}/forms?formId=${formId.toLowerCase()}&formVersion=${formVersion}` - ); - - // Ensure the request was successful - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - // Extract and format data from the API response - const data = await response.json(); - const formattedResult: FormResult = { - version: formVersion, - data, - }; - - return formattedResult; - } catch (error) { - // Handle error if API request fails - console.error(`Error fetching data for formId: ${formId}, version: ${formVersion}`); - console.error(error); - return null; - } - }) - ); - } - } - - return resultObject; - } - -export function generateDocs(obj: any, results: any = [], parentName: string = '', prompt: string = '') { - if (typeof obj === 'object' && obj !== null) { - if ('rhf' in obj) { - const resultItem: any = { rhf: obj.rhf }; - - if ('label' in obj) { - resultItem.label = obj.label; - } - - if ('name' in obj) { - resultItem.name = obj.name; - } - - if ((obj.rhf === 'Select' || obj.rhf === 'Radio') && obj.props) { - resultItem.options = [] - obj.props?.options.forEach((field: any) => { - resultItem.options?.push(field.value) - }) - } - - resultItem.parentName = parentName; - resultItem.prompt = prompt; - - results.push(resultItem); - } - - for (const key in obj) { - if (obj.hasOwnProperty(key)) { - if ('name' in obj) { - parentName = obj.name; - } - if ('description' in obj) { - prompt = obj.description; - } - generateDocs(obj[key], results, parentName, prompt); - } - } - } -} diff --git a/docs/_deploy-metrics/lib/getAwsResources.ts b/docs/_deploy-metrics/lib/getAwsResources.ts deleted file mode 100644 index ebadf30e7a..0000000000 --- a/docs/_deploy-metrics/lib/getAwsResources.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { octokit } from "./octokit"; -import { getRepoName } from "./getRepoName"; -import JSZip from 'jszip'; - -export interface Resource { - PhysicalResourceId: string - ResourceType: string - ResourceStatus: string - LogicalResourceId: string - LastUpdatedTimestamp: string - StackName: string -} - -const unzip = async (blob: string) => { - const zip = await JSZip.loadAsync(blob); - const data = zip.file('aws-resources.json')?.async('string') - return data -} - -export const getAwsResources = async (branch: string) => { - - // get a list of the artifacts created by the deploy step - const artifacts = await octokit.paginate( - "GET /repos/{owner}/{repo}/actions/artifacts", - { - owner: "Enterprise-CMCS", - repo: getRepoName, - per_page: 100, - name: 'aws-resources-' + branch - }, - (res) => res.data.flat() - ); - - // sort them by most recent - artifacts.sort((a, b) => { - const dateA = new Date(a.created_at as string); - const dateB = new Date(b.created_at as string); - return dateB.getTime() - dateA.getTime(); - }); - - if (!artifacts[0]) { - throw 'No artifact found' - } - - // get the downloadable zip file - const response = await octokit.request( - `GET /repos/{owner}/{repo}/actions/artifacts/${artifacts[0].id}/zip`, - { - owner: "Enterprise-CMCS", - repo: getRepoName, - } - ); - - const data = await unzip(response.data) as string - - return JSON.parse(data) as unknown as Resource[] - -}; diff --git a/docs/_deploy-metrics/lib/getFilterOptions.ts b/docs/_deploy-metrics/lib/getFilterOptions.ts deleted file mode 100644 index 4b35e299c1..0000000000 --- a/docs/_deploy-metrics/lib/getFilterOptions.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { FilterOption } from "../components/CheckboxFilterPopover"; -import { Resource } from "./getAwsResources"; - -export function getTypeOptions(resources: Resource[]): FilterOption[] { - const counts: Record = {}; - for (const resource of resources) { - const parts = resource.ResourceType.split("::"); - const type = parts[1]; - if (!counts[type]) { - counts[type] = 0; - } - counts[type]++; - } - const result: FilterOption[] = []; - for (const type in counts) { - result.push({ - label: type, - value: `AWS::${type}`, - count: counts[type], - }); - } - return result; - } - - export function getStackOptions(data: Resource[]): FilterOption[] { - const resourceCounts = data.reduce( - (counts: Record, stack: Resource) => { - const stackName = stack.StackName; - if (!counts[stackName]) { - counts[stackName] = 0; - } - counts[stackName]++; - return counts; - }, - {} - ); - - return Object.keys(resourceCounts).map((stackName: string) => { - return { - label: stackName, - value: stackName, - count: resourceCounts[stackName], - }; - }); - } \ No newline at end of file diff --git a/docs/_deploy-metrics/lib/getMeanTimeToRecover.ts b/docs/_deploy-metrics/lib/getMeanTimeToRecover.ts deleted file mode 100644 index 1deb8dfe16..0000000000 --- a/docs/_deploy-metrics/lib/getMeanTimeToRecover.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { octokit } from "./octokit"; -import differenceInHours from "date-fns/differenceInHours"; -import { getRepoName } from "./getRepoName"; - -export const getMeanTimeToRecover = async (branch: string) => { - const response = await octokit.paginate( - "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", - { - owner: "Enterprise-CMCS", - repo: getRepoName, - workflow_id: "deploy.yml", - branch, - per_page: 100, - }, - (response) => response.data.flat() - ); - - const getTimes = (jobs: typeof response) => { - const result = []; - let isFailed = false; - let timestamp = ""; - - for (const entry of jobs.reverse()) { - if (entry.conclusion === "failure" && !timestamp) { - isFailed = true; - timestamp = entry.created_at; - } - if (isFailed && entry.conclusion === "success") { - isFailed = false; - result.push({ - upTime: new Date(entry.created_at), - failedTime: new Date(timestamp), - }); - timestamp = ""; - } - } - return result; - }; - const getMeanFromTimes = (times: { failedTime: Date; upTime: Date }[]) => { - if (times.length === 0) return 0; - - return ( - times.reduce((prev, current) => { - const diff = differenceInHours(current.upTime, current.failedTime); - - prev += diff; - return prev; - }, 0) / times.length - ); - }; - - return getMeanFromTimes(getTimes(response)); -}; diff --git a/docs/_deploy-metrics/lib/getPrsToBranch.ts b/docs/_deploy-metrics/lib/getPrsToBranch.ts deleted file mode 100644 index a3c1ef47eb..0000000000 --- a/docs/_deploy-metrics/lib/getPrsToBranch.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { octokit } from "./octokit"; -import differenceInHours from "date-fns/differenceInHours"; -import { getRepoName } from "./getRepoName"; - -export const getPrsToBranch = async (branch: string) => { - const data = await octokit.paginate( - "GET /repos/{owner}/{repo}/pulls", - { - owner: "Enterprise-CMCS", - repo: getRepoName, - state: "closed", - per_page: 100, - base: branch, - }, - (res) => res.data.flat() - ); - - const timesToMergePrs = data - .filter((pr) => pr.merged_at && pr.created_at) - .map((pr) => ({ - hours: differenceInHours( - new Date(pr.merged_at!), - new Date(pr.created_at!) - ), - })); - const averageTimeToMerge = - timesToMergePrs.reduce((total, { hours }) => total + hours, 0) / - timesToMergePrs.length || 0; - - return { timesToMergePrs, averageTimeToMerge }; -}; diff --git a/docs/_deploy-metrics/lib/getRepoName.ts b/docs/_deploy-metrics/lib/getRepoName.ts deleted file mode 100644 index 8d2ae4fdca..0000000000 --- a/docs/_deploy-metrics/lib/getRepoName.ts +++ /dev/null @@ -1,3 +0,0 @@ -import packageJson from "../../../package.json"; - -export const getRepoName = packageJson.name; diff --git a/docs/_deploy-metrics/lib/getSuccessfulDeploys.ts b/docs/_deploy-metrics/lib/getSuccessfulDeploys.ts deleted file mode 100644 index 8489ce56c9..0000000000 --- a/docs/_deploy-metrics/lib/getSuccessfulDeploys.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getRepoName } from "./getRepoName"; -import { octokit } from "./octokit"; - -export const getSuccessfulDeploys = async (branch: string) => { - const data = await octokit.paginate( - "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", - { - owner: "Enterprise-CMCS", - repo: getRepoName, - workflow_id: "deploy.yml", - branch, - per_page: 100, - }, - (res) => res.data.flat() - ); - - const failedRuns = data.filter((run) => run.conclusion !== "success").length; - const passedRuns = data.length - failedRuns; - - return { failedRuns, passedRuns }; -}; diff --git a/docs/_deploy-metrics/lib/octokit.ts b/docs/_deploy-metrics/lib/octokit.ts deleted file mode 100644 index 2204a66b14..0000000000 --- a/docs/_deploy-metrics/lib/octokit.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Octokit } from "octokit"; -import { createActionAuth } from "@octokit/auth-action"; - -export const octokit = new Octokit({ - authStrategy: process.env.PAT_TOKEN ? undefined : createActionAuth, - auth: process.env.PAT_TOKEN, -}); - -export const octokitBranchesToUse = - process.env.BRANCHES_TO_GENERATE?.split(","); diff --git a/docs/_deploy-metrics/next.config.js b/docs/_deploy-metrics/next.config.js deleted file mode 100644 index f53350210e..0000000000 --- a/docs/_deploy-metrics/next.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint no-undef: 0 */ // --> OFF - -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, - swcMinify: true, - assetPrefix: "./", -}; - -module.exports = { - ...nextConfig, -}; diff --git a/docs/_deploy-metrics/package.json b/docs/_deploy-metrics/package.json deleted file mode 100644 index 7b507f9bf4..0000000000 --- a/docs/_deploy-metrics/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "deploy-metrics", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build && next export -o ../metrics", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "@chakra-ui/react": "^2.3.6", - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@octokit/auth-action": "^2.0.2", - "@octokit/types": "^8.0.0", - "d3-color": "3.1.0", - "date-fns": "^2.29.3", - "framer-motion": "^6", - "jszip": "^3.10.1", - "next": "^13.5.4", - "octokit": "^3.2.1", - "react": "18.3.1", - "react-aws-icons": "^1.2.1", - "react-dom": "18.3.1", - "react-icons": "^4.8.0", - "react-json-to-csv": "^1.2.0", - "recharts": "^2.12.7" - }, - "devDependencies": { - "@types/node": "18.11.0", - "@types/react": "^18.2.21", - "@types/react-dom": "18.0.6", - "eslint": "8.25.0", - "eslint-config-next": "12.3.1", - "typescript": "4.8.4" - } -} diff --git a/docs/_deploy-metrics/pages/_app.tsx b/docs/_deploy-metrics/pages/_app.tsx deleted file mode 100644 index d5806bc9c9..0000000000 --- a/docs/_deploy-metrics/pages/_app.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { AppProps } from "next/app"; -import { ChakraProvider } from "@chakra-ui/react"; - -function MyApp({ Component, pageProps }: AppProps) { - const AnyComponent = Component as any; - return ( - - - - ); -} - -export default MyApp; diff --git a/docs/_deploy-metrics/pages/aws/index.tsx b/docs/_deploy-metrics/pages/aws/index.tsx deleted file mode 100644 index 550eea7773..0000000000 --- a/docs/_deploy-metrics/pages/aws/index.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import type { InferGetStaticPropsType } from "next"; -import { getAwsResources } from "../../lib/getAwsResources"; -import * as UI from "@chakra-ui/react"; -import { Resources } from "../../components/Resources"; -import { octokitBranchesToUse } from "../../lib/octokit"; -import { useState } from "react"; -import packageJson from "../../../../package.json"; - -export const getStaticProps = async () => { - const branchData: { - [name: string]: { - resources: Awaited>; - }; - } = {}; - - for (const branch of octokitBranchesToUse!) { - const resources = await getAwsResources(branch); - - branchData[branch] = { - resources, - }; - } - - return { - props: { - branchData, - branches: octokitBranchesToUse, - repoName: process.env?.REPO_NAME ?? packageJson.name.toUpperCase(), - }, - }; -}; - -const Aws = ({ - branchData, - repoName, - branches, -}: InferGetStaticPropsType) => { - const [selectedBranch, setSelectedBranch] = useState(branches![0]); - - const { resources } = branchData[selectedBranch]; - - return ( - - -
- - AWS Resources - - - {repoName} - -
- - Currently viewing data for - { - setSelectedBranch(newValue.currentTarget.value); - }} - > - {branches?.map((branch, index) => ( - - ))} - - -
- - {resources && ( - - )} -
- ); -}; - -export default Aws; diff --git a/docs/_deploy-metrics/pages/dora/index.tsx b/docs/_deploy-metrics/pages/dora/index.tsx deleted file mode 100644 index 17648668b5..0000000000 --- a/docs/_deploy-metrics/pages/dora/index.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import type { InferGetStaticPropsType } from "next"; -import { Cards } from "../../components/Cards"; -import { getMeanTimeToRecover } from "../../lib/getMeanTimeToRecover"; -import { getPrsToBranch } from "../../lib/getPrsToBranch"; -import { getSuccessfulDeploys } from "../../lib/getSuccessfulDeploys"; -import { - Divider, - Container, - Heading, - Text, - Select, - HStack, -} from "@chakra-ui/react"; -import { LeadTimeForChanges } from "../../components/LeadTimeForChanges"; -import { DeploymentFrequency } from "../../components/DeploymentFrequency"; -import { octokitBranchesToUse } from "../../lib/octokit"; -import { useState } from "react"; -import packageJson from "../../../../package.json"; - -export const getStaticProps = async () => { - const branchData: { - [name: string]: { - meanTimeToRecover: Awaited>; - prsToBranch: Awaited>; - successfulDeploys: Awaited>; - }; - } = {}; - - for (const branch of octokitBranchesToUse!) { - const meanTimeToRecover = await getMeanTimeToRecover(branch); - const prsToBranch = await getPrsToBranch(branch); - const successfulDeploys = await getSuccessfulDeploys(branch); - - branchData[branch] = { - meanTimeToRecover, - prsToBranch, - successfulDeploys, - }; - } - - return { - props: { - branchData, - branches: octokitBranchesToUse, - repoName: process.env?.REPO_NAME ?? packageJson.name.toUpperCase(), - }, - }; -}; - -const Dora = ({ - branchData, - repoName, - branches, -}: InferGetStaticPropsType) => { - const [selectedBranch, setSelectedBranch] = useState(branches![0]); - - const dataForBranch = branchData[selectedBranch]; - - return ( - - Deployment Metrics for {repoName} - - Currently viewing data for - - - - - - - - - ); -}; - -export default Dora; diff --git a/docs/_deploy-metrics/public/favicon.ico b/docs/_deploy-metrics/public/favicon.ico deleted file mode 100644 index 4b60c0d6dc..0000000000 Binary files a/docs/_deploy-metrics/public/favicon.ico and /dev/null differ diff --git a/docs/_deploy-metrics/tsconfig.json b/docs/_deploy-metrics/tsconfig.json deleted file mode 100644 index a798452985..0000000000 --- a/docs/_deploy-metrics/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] -} diff --git a/docs/_deploy-metrics/types.d.ts b/docs/_deploy-metrics/types.d.ts deleted file mode 100644 index cd55c5c844..0000000000 --- a/docs/_deploy-metrics/types.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare module 'react-json-to-csv' -declare module 'react-aws-icons/dist/aws/logo/AWS' \ No newline at end of file diff --git a/docs/assets/.gitignore b/docs/assets/.gitignore deleted file mode 100644 index d8c08bfb0b..0000000000 --- a/docs/assets/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ - -# Diagram updates with draw.io can create temporary backup files that we want to ignore. -*.bkp \ No newline at end of file diff --git a/docs/assets/diagram.svg b/docs/assets/diagram.svg deleted file mode 100644 index 1d3d2ce5d8..0000000000 --- a/docs/assets/diagram.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - OktaKibana \ No newline at end of file diff --git a/docs/assets/setup.sh b/docs/assets/setup.sh deleted file mode 100755 index 7fb7c1dc71..0000000000 --- a/docs/assets/setup.sh +++ /dev/null @@ -1,150 +0,0 @@ -set -e - -# Check that we're on a mac. -if [[ ! "$OSTYPE" =~ ^darwin ]]; then - echo "ERROR: This script is intended only for MacOS." && exit 1 -fi - -# Check that XCode Command Line Tools are installed. -if ! xcode-select -p > /dev/null; then - echo "ERROR: XCode Command Line Tools must be installed on this machine before running this script, but were not found." && exit 1 -fi - -# Determine what shell and rc file we might want to modify -shell="" -shellprofile="" -macprorcfile="" -if [ "$CI" != "true" ]; then - echo "Which terminal shell do you want to configure? Please input a number and hit Enter:" - select selectedshell in zsh bash - do - case $selectedshell in - "zsh") - shell=$selectedshell - shellprofile="$HOME/.zshenv" - macprorcfile="$HOME/.macprorc" - ;; - - "bash") - shell=$selectedshell - macprorcfile="$HOME/.macprorc" - if test -f "$HOME/.bash_profile"; then - shellprofile="$HOME/.bash_profile" - else - shellprofile="$HOME/.bashrc" - fi - ;; - *) - echo "ERROR: Invalid input. Exiting." - exit 1 - ;; - esac - break - done -else - shell="bash" - shellprofile="/tmp/.profile" - macprorcfile="/tmp/.macprorc" -fi -touch $macprorcfile -touch $shellprofile - -# Set some things based on chip architecture -arch=`uname -m` -homebrewprefix="" -if [ "$arch" == "arm64" ]; then - # If we're on Apple Silicon, check that Rosetta 2 has already been installed and is running. - if ! /usr/bin/pgrep -q oahd; then - echo "ERROR: Rosetta must be installed on this machine before running this script, but was not found." && exit 1 - fi - homebrewprefix="/opt/homebrew" -else - homebrewprefix="/usr/local" -fi - -# Install HomeBrew, an OSX package manager -if ! which brew > /dev/null ; then - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -fi - -export PATH="$homebrewprefix:$PATH" - -# Install the AWS CLI, used to interact with any/all AWS services -if ! which aws > /dev/null ; then - brew install awscli session-manager-plugin -fi - -# Install jq, a command line utility for parsing JSON. -if ! which jq > /dev/null ; then - brew install jq -fi - -# Install nvm, a version manager for Node, allowing multiple versions of Node to be installed and used -if [ "$CI" != "true" ]; then - if [ ! -f ~/.nvm/nvm.sh ]; then - brew install nvm - fi -else - brew install nvm -fi -mkdir -p ~/.nvm - -# Install awslogs, a utility for streaming CloudWatch logs -if ! which awslogs > /dev/null ; then - brew install awslogs -fi - -# Install bun, a super fast package manager for node -if ! which bun > /dev/null ; then - brew install oven-sh/bun/bun -fi - -# Install git, our version control system -if ! which git > /dev/null ; then - brew install git -fi - -# Install docker, our container engine of choice -if ! which docker > /dev/null ; then - brew install docker -fi - -# Install colima, a container runtime in which we can run Docker images -if ! which colima > /dev/null ; then - brew install colima -fi - -# Install and configure direnv, a tool for automatically setting environment variables -if ! which direnv > /dev/null ; then - brew install direnv -fi - -# Install kion-cli, a go package used to authenticate to Kion and access AWS -if ! which kion > /dev/null ; then - brew install kionsoftware/tap/kion-cli -fi -touch ~/.kion.yml - -touch $macprorcfile -echo """ -### MANAGED BY MACPRO Workspace Setup - (DO NOT EDIT THIS FILE) - -export NVM_DIR="$HOME/.nvm" - [ -s "$homebrewprefix/opt/nvm/nvm.sh" ] && \. "$homebrewprefix/opt/nvm/nvm.sh" # This loads nvm - [ -s "$homebrewprefix/opt/nvm/etc/bash_completion.d/nvm" ] && \. "$homebrewprefix/opt/nvm/etc/bash_completion.d/nvm" # This loads nvm bash_completion - -export PATH="$homebrewprefix/bin:\$PATH" -eval \"\$($homebrewprefix/bin/brew shellenv)\" - -eval \"\$(direnv hook $shell)\" -""" > $macprorcfile - -if ! cat $shellprofile | grep -q '### MANAGED BY MACPRO Workspace Setup - source - (DO NOT EDIT)'; then - echo """ -### MANAGED BY MACPRO Workspace Setup - source - (DO NOT EDIT) -if [ -f $macprorcfile ]; then - source $macprorcfile -fi -### MANAGED BY MACPRO Workspace Setup - source - (DO NOT EDIT) -""" >> $shellprofile -fi \ No newline at end of file diff --git a/docs/docs/design/authentication_and_authorization.md b/docs/docs/design/authentication_and_authorization.md deleted file mode 100644 index de0c12cc44..0000000000 --- a/docs/docs/design/authentication_and_authorization.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: default -title: Auth -parent: Design ---- - -# Authentication and Authorization -{: .no_toc } - - -The {{ site.repo.name }} project caters to both CMS and State users, each requiring specific permissions to ensure secure access and compliance with CMS policies. This page outlines the design and hierarchy of roles within our system, focusing on the allocation of permissions rather than the underlying authentication technology. - - -## Detail - -We have seven distinct user roles within our system, categorized into application roles (app roles) and IDM roles.\ -**App Roles** include: - -- **onemac-micro-statesubmitter** -- **onemac-micro-readonly** -- **onemac-micro-reviewer** -- **onemac-micro-helpdesk** - -These roles authorize privileges within the OneMAC Micro application. Importantly, a user can be assigned only one of these roles at any given time. These roles are defined within IDM by the user's custom attribute custom:cms-roles and are essential for operational functionality within the application.\ -**IDM Roles** consist of: - -- **onemac-micro-sysadmin** -- **onemac-micro-roleapprover** -- **onemac-micro-statesysadmin** - -IDM roles grant privileges within IDM itself, focusing on role request approvals rather than direct application access. A user can hold one of these roles at any given time, with specific restrictions regarding their combination: - -- Users cannot hold a mix of state and CMS IDM roles. For example, if a user holds the **onemac-micro-statesysadmin** role, they cannot hold a CMS IDM role. -- Conversely, if a user has a CMS IDM role, they cannot hold the **onemac-micro-statesysadmin** role. However, a user could hold both CMS IDM roles (**onemac-micro-sysadmin** and **onemac-micro-roleapprover**) simultaneously. - -**Authentication vs. Authorization:** - -- **Authentication:** Anyone with an active IDM account can log in to the OneMAC Micro application. If they lack one of the four app roles, they will be informed via a banner that they need to request a role through IDM. -- **Authorization:** Only users with an app role are authorized to perform actions within the application. - -Moreover, it is possible for a user to have one app role and one or more IDM roles concurrently. This setup allows for users who manage IDM's administrative tasks to also use the application, provided they have the necessary app role.\ -In conclusion, while any IDM account holder can access the OneMAC Micro application, only those with an app role are granted operational privileges. IDM roles serve administrative functions within IDM and have specific combination restrictions to ensure clear role delineation. \ No newline at end of file diff --git a/docs/docs/design/design.md b/docs/docs/design/design.md deleted file mode 100644 index 93638f9423..0000000000 --- a/docs/docs/design/design.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -layout: default -title: Design -nav_order: 3 -has_children: true -permalink: docs/design ---- - -# Design -{: .no_toc } - -This is a place where you can find more about the design and history of different pieces of the application. -{: .fs-6 .fw-300 } - -The {{ site.repo.name }} project is a [serverless monorepo](https://serverless-stack.com/chapters/organizing-serverless-projects.html). It is, for the most part, a collection of standalone Serverless Framework micro services bound together in a repository. Loose coupling of the micro services is facilitated using one or several tools, which include CloudFormation outputs, AWS Systems Manager Parameter Store paramters, and AWS Secrets Manager stores. This section will describe each service in a high level of detail. diff --git a/docs/docs/developer-guide/aws-auth.md b/docs/docs/developer-guide/aws-auth.md deleted file mode 100644 index b5250ae6cd..0000000000 --- a/docs/docs/developer-guide/aws-auth.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -layout: default -title: AWS Login -parent: Developer Guide -nav_order: 1 ---- - -# AWS Login -{: .no_toc } - -Authenticating to an AWS account(s) is a required first step for many workflows. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} ---- - -### AWS Console Login - -#### Summary -This procedure will take you to the AWS Console in a web browser, for one of the AWS accounts used by this project. - -#### Prerequisites -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -To get to the AWS Console: -- Login to the cloud VPN, https://cloudvpn.cms.gov -- Go to the CMS [Kion (Cloudtamer) site](https://cloudtamer.cms.gov/login). This is a great link to bookmark. Note: if the Kion site fails to load in your browser, it is very likely an issue with your VPN. The Kion site is only accessibly while actively on the VPN. -- Login with your CMS EUA credentials. -- Select the drop down menu next to the appropriate account. -- Select Cloud Access Roles -- Select the role you wish to assume. -- Select Web Access. The AWS Console for the account should open in a new browser tab. Once the console is open, you may close your VPN connection, if you wish. - -#### Notes -- Once connected to the AWS Console, you can close your VPN connection if you'd like. The VPN is only needed when authenticating to Kion and gaining AWS credentials. -- Your browser session is valid for up to 4 hours. After 4 hours, you will need to redo this procedure. - -### AWS CLI credentials - -#### Summary -This procedure will show you how to retrieve AWS CLI credentils for one of the AWS accounts used by this project, granting you programmatic access to AWS. This is required for any operations you may run directly against AWS. - -#### Prerequisites -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- Login to the cloud VPN, https://cloudvpn.cms.gov -- Go to the CMS [Kion (Cloudtamer) site](https://cloudtamer.cms.gov/login). This is a great link to bookmark. Note: if the Kion site fails to load in your browser, it is very likely an issue with your VPN. The Kion site is only accessibly while actively on the VPN. -- Login with your CMS EUA credentials. -- Select the drop down menu next to the appropriate account. -- Select Cloud Access Roles -- Select the role you wish to assume. -- Select 'Short-term Access Keys'. -- Click the code block under 'Option 1', to copy the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN environment variables to your clipboard. -- Navigate to a terminal on your mac, and paste the credentials. You should now be able to interact with AWS programmatically, based on the role you selected in Kion. - -#### Notes -- There are three available options when getting access keys from Kion. The instructions above detail Option 1, which is essentially copying and pasting env variables to a terminal. Feel free to use one of the other options if you'd prefer. For sake of simplicity, Option 1 will be the only one documented and supported here. -- Once you have credentials from Kion, you can close your VPN connection if you'd like. The VPN is only required when talking to Kion to obtain credentials. -- The credentials are valid for 4 hours, after which you'll need to redo this procedure. \ No newline at end of file diff --git a/docs/docs/developer-guide/base-update.md b/docs/docs/developer-guide/base-update.md deleted file mode 100644 index fb27abb138..0000000000 --- a/docs/docs/developer-guide/base-update.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: default -title: Update from Base -parent: Developer Guide -nav_order: 9 ---- - -# Update from Base -{: .no_toc } - -How to update your project with the latest macpro-base-template releases. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -### Update from Base - -#### Summary -This command fetches the latest macpro-base-template changes and merges them into your branch. You may then resolve any merge conflicts, and open a PR. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- Use the run script: - ```bash - nvm use - run base-update - ``` - -#### Notes -- None diff --git a/docs/docs/developer-guide/deploy.md b/docs/docs/developer-guide/deploy.md deleted file mode 100644 index 28be6ab5f8..0000000000 --- a/docs/docs/developer-guide/deploy.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -layout: default -title: Deploy a Stage -parent: Developer Guide -nav_order: 2 ---- - -# Deploy a Stage -{: .no_toc } - -How-to deploy a new or existing stage to AWS. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -### Deploy a stage - -#### Summary -This deploys the entire application, so the entire stage, to AWS. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- [Obtain and set AWS CLI credentials]({{ site.baseurl }}{%link docs/developer-guide/aws-auth.md %}) -- Deploy using the run script: - ```bash - cd {{ site.repo.name }} - nvm use - run deploy --stage foo - ``` - -#### Notes -- None - -### Deploy an individual service - -#### Description -This will deploy a single service for a given stage. All other services on which your target service is dependent must already be deployed for the stage. For example: if service B depends on service A, and you want to use this procedure to deploy only service B, then service A must have already been deployed. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- [Obtain and set AWS CLI credentials]({{ site.baseurl }}{%link docs/developer-guide/aws-auth.md %}) -- Deploy using the run script: - ```bash - cd {{ site.repo.name }} - nvm use - run deploy --service bar --stage foo - ``` - -#### Notes -- None - -### Deploy using GitHub Actions - -#### Summary -This project uses [GitHub Actions](https://github.com/features/actions) as its CI/CD tool. For the most part, this project also adheres to [GitOps](https://www.gitops.tech/). That said... - -Each branch pushed to the {{ site.repo.name }} git repository is automatically deployed to AWS. GitHub Actions sees the 'push' event of a new branch, and runs our Deploy.yml workflow. After a few minutes, the branch will be fully deployed. This 1:1 relationship between git branches and deployed stages is the reason why 'stage' and 'branch' are sometimes used interchangeably to refer to a deployed set of the application. - -#### Prerequisites: -- Git repo write access; complete the Git access request portion of [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- [Obtain and set AWS CLI credentials]({{ site.baseurl }}{%link docs/developer-guide/aws-auth.md %}) -- Create a new branch based off of any other branch or commit. The 'main' branch is the most common branch from which to create new branches, and is shown in the following procedure.: - ```bash - cd {{ site.repo.name }} - git checkout main - git pull - git checkout -b foo - git push --set-upstream origin foo - ``` -- Monitor the status of your branch's deployment in the repo's [Actions area](https://github.com/{{ site.repo.org }}/{{ site.repo.name }}/actions). - -#### Notes -- None \ No newline at end of file diff --git a/docs/docs/developer-guide/destroy.md b/docs/docs/developer-guide/destroy.md deleted file mode 100644 index b0c5ee781e..0000000000 --- a/docs/docs/developer-guide/destroy.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -layout: default -title: Destroy a Stage -parent: Developer Guide -nav_order: 3 ---- - -# Destroy a Stage -{: .no_toc } - -How-to destroy a stage in AWS. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -### Destroy using GitHub Actions - branch deletion - -#### Summary -GitHub Actions is usually the best way to destroy a stage. A Destroy workflow exists for this project, which will neatly take down any and all infrastructure related to a branch/stage, as well as deactivate the GitHub Environment, if it exists. - -In most cases, stages are deployed from a branch in the git repo. If this is the case, and if the branch can be safely deleted, destroying using GitHub Actions and branch deletion is the preferred approach. - -#### Prerequisites -- Git repo write access; complete the Git access request portion of [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- Stop and think about what you are doing. Destroying is a lot easier to avoid then to undo. -- Delete the branch for the stage you wish to delete. - ```bash - cd {{ site.repo.name }} - git push --delete origin foo - ``` -- Monitor the status of your stage's destruction in the repo's [Actions area](https://github.com/{{ site.repo.org }}/{{ site.repo.name }}/actions). - -#### Notes -- None - -### Destroy using GitHub Actions - manual dispatch - -#### Summary -The same GitHub Actions workflow referenced above can be triggered manually. This is primarily useful if there is AWS infrastructure that still exists for a branch that has been deleted, and you don't want to go to the trouble of running destroy from your Mac. Or, if you want to do a clean deploy of a stage, but you don't want to delete the branch, this can also be handy. - -#### Prerequisites -- Git repo write access; complete the Git access request portion of [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- In a browser, go to the [repo](https://github.com/{{ site.repo.org }}/{{ site.repo.name }}) -- Click the Actions tab -- Click Destroy, located on the left hand side of the screen. -- Click 'Run workflow' - - Leave 'Use workflow from' set to main. - - Enter the name of the stage you wish to destroy in the free text field. - - Click 'Run workflow' -- Monitor the status of your stage's destruction in the repo's [Actions area](https://github.com/{{ site.repo.org }}/{{ site.repo.name }}/actions). - -#### Notes -- None - -### Destroy a stage - -#### Summary -This destroys an entire application, so the entire stage, to AWS. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- Stop and think about what you are doing. Destroying is a lot easier to avoid then to undo. -- [Obtain and set AWS CLI credentials]({{ site.baseurl }}{%link docs/developer-guide/aws-auth.md %}) -- Destroy using the run script: - ```bash - cd {{ site.repo.name }} - nvm use - run destroy --stage foo - ``` - -#### Notes -- After running the above destroy command, the script will output any Cloudformation stacks that will be deleted, and ask you to verify the stage name to proceed with destruction. If you'd like to proceed, re-enter the stage name and hit enter. -- The destroy script will hold your terminal process open until all stacks report as DESTROY_COMPLETE in cloudformation. If a stack fails to delete, or if there is a timeout, the script will fail. You may retry the script again, but it may be worth investigating the failure. -- Please be mindful of what you are doing. - -### Destroy an individual service - -#### Summary - -This will destroy a single service for a given stage. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- Stop and think about what you are doing. Destroying is a lot easier to avoid then to undo. -- [Obtain and set AWS CLI credentials]({{ site.baseurl }}{%link docs/developer-guide/aws-auth.md %}) -- Destroy a single service using the run script: - ```bash - cd {{ site.repo.name }} - nvm use - run destroy --service bar --stage foo - ``` - -#### Notes -- All notes from the Destroy a Stage section (above) hold true for destroying an individual service. diff --git a/docs/docs/developer-guide/development-guide.md b/docs/docs/developer-guide/development-guide.md deleted file mode 100644 index 330047a539..0000000000 --- a/docs/docs/developer-guide/development-guide.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -layout: default -title: Developer Guide -nav_order: 4 -has_children: true -permalink: docs/developer-guide ---- - -# Developer Guide -{: .no_toc } - -How to do common things as a developer on the project -{: .fs-6 .fw-300 } - -Let this serve as a running list of practical, operational how-to guides for developers. If something is missing, please add it! Thanks. - diff --git a/docs/docs/developer-guide/docs.md b/docs/docs/developer-guide/docs.md deleted file mode 100644 index afd996facc..0000000000 --- a/docs/docs/developer-guide/docs.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: default -title: Run Docs Site Locally -parent: Developer Guide -nav_order: 4 ---- - -# Run Jekyll Docs Site Locally -{: .no_toc } - -How-to run our GitHub Pages Jekyll docs site (the site you're viewing) locally. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -### Start the docs site locally - -#### Summary -This procedure will walk you through starting up the docs site locally. You may want to run the docs site locally if you are modifying the documentation, allowing you to preview before committing. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) -- You will need a docker runtime up and running. This can be Docker Desktop (if licensed), or an open source tool like Colima. Generally, if you can run the command `docker ps` and not get an error, you have a docker runtime up and running. - -#### Procedure -- Start the docs site using the run script: - ```bash - cd {{ site.repo.name }} - run docs - ``` -- In a browser, visit [http://localhost:4000](http://localhost:4000) to view the running site. -- As you make changes to the files under the repo's docs folder, you should see the changes reflected in your browser in less than a second. - -#### Notes diff --git a/docs/docs/developer-guide/e2e-testing.md b/docs/docs/developer-guide/e2e-testing.md deleted file mode 100644 index 935ed00cdd..0000000000 --- a/docs/docs/developer-guide/e2e-testing.md +++ /dev/null @@ -1,35 +0,0 @@ -layout: default -title: End-to-end Testing -parent: Developer Guide -nav_order: 11 - -# e2e testing using Playwright -{: .no_toc } - -## Table of Contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -## What is Playwright -The Playwright testing framework provides a powerful set of tools and APIs for automating and testing web applications. This guide will introduce you to the basics of using the Playwright framework and its core concepts. - -The Playwright framework allows you to write tests that simulate user interactions with web pages, such as clicking buttons, filling forms, and validating page content. It supports multiple web browsers, including Chromium, Firefox, and WebKit, enabling cross-browser testing. - -## How do I run tests -As a developer you run e2e tests against a local running instance of the application. By using the `run ui --stage [stage]` command to deploy and init an instance of the application. A second terminal can be used to run the `run e2e` command which will run the playwright tests against the currently running instance of the ui on localhost:5000. - -## How do I write tests -The e2e tests for the UI service are located in `src/services/ui/e2e` and defined in .spec files. Use the examples found to guide how to structure your tests. - -When defining the selectors for elements make sure to define your selectors in the `e2e/selectors` directory. Defining selectors using react component names and properties is prefered to maintain consistent scalable testing. Other patterns can be used such as xpath, element type/name, and id/data-id/test-id. But should only be used when necessary. - -## Running in the pipeline -An action has been added to the deploy github actions workflow that runs the e2e tests after the application deployment against the deployed frontend url. Note: This action will run in all environments except production. - -## Additional Notes: - -Playwright is very powerful but should be used only to test general purpose user journeys. A test should NOT be create to test every ticket or story. Please review the documentation for additional functionality: [Playwright Docs](https://playwright.dev/docs/intro) \ No newline at end of file diff --git a/docs/docs/developer-guide/launch-darkly.md b/docs/docs/developer-guide/launch-darkly.md deleted file mode 100644 index 6a1d27003e..0000000000 --- a/docs/docs/developer-guide/launch-darkly.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: default -title: Launch Darkly -parent: Developer Guide -nav_order: 1 ---- - -# Launch Darkly -{: .no_toc } - -LaunchDarkly is a feature management platform that enables software teams to effectively manage and control the lifecycle of features in their applications. It provides feature flagging, experimentation, and continuous delivery capabilities, allowing teams to release features with confidence and control. - -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} ---- - -### AWS Console Login - -#### Summary -These instructions will help you get started with Launch Darkly. - -#### Prerequisites -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -To get to the AWS Console: -- Login to the cloud VPN, https://cloudvpn.cms.gov -- Follow these instructions to get access to the Launch Darkly client [LaunchDarkly Guide](https://confluenceent.cms.gov/display/MDSO/LD+Guide). This is a great link to bookmark. Your EUA account will need the correct job codes before being able to access the LaunchDarkly client. -- Once you have access to LaunchDarkly you will need to check if you have the correct user roles to access all three environments for MACPRO_MAKO. If not, Keith Littlejohn is our CMS LaunchDarkly point of contact. - -#### Notes -- Once you have access to LaunchDarkly you can switch on feature flags which should show/hide various features. -- Access codes for UI, API, and mobile all live in AWS System Parameter Store. They were ddistrubted by Keith Littlejohn. We do not have access to the environment's api keys. If we need to rotate them please reach out to him. -- There is three environments in LaunchDarkly for Mako: Dev, IMPL, and Production which point to our three environments. Dev is used for our ephemeral environments. diff --git a/docs/docs/developer-guide/list-running-stages.md b/docs/docs/developer-guide/list-running-stages.md deleted file mode 100644 index 6428030fb8..0000000000 --- a/docs/docs/developer-guide/list-running-stages.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -layout: default -title: List Running Stages -parent: Developer Guide -nav_order: 9 ---- - -# List Running Stages -{: .no_toc } - -How to get a list of currently running stages for this project in the current AWS account. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -### List Running Stages - -#### Summary -This returns a list of currently running stages for this project in the current AWS account. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- [Obtain and set AWS CLI credentials]({{ site.baseurl }}{%link docs/developer-guide/aws-auth.md %}) -- Use the run script: - ```bash - nvm use - run listRunningStages - ``` - -#### Notes -- None - -### Run Report using GitHub Actions - -#### Summary -This project uses [GitHub Actions](https://github.com/features/actions) as its CI/CD tool. - -Each of our repositories has a GitHub Actions workflow added to run this list running stages command and report the results to slack on a schedule. This workflow may also be manually invoked. - -#### Prerequisites: -- Git repo access; complete the Git access request portion of [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) -- Access to CMS slack to see the generated report. - -#### Procedure -- Browse to the actions page of the repository in GitHub, select the "Running Stage Notifier" workflow and press run workflow. - -#### Notes -- None diff --git a/docs/docs/developer-guide/pre-commit.md b/docs/docs/developer-guide/pre-commit.md deleted file mode 100644 index e609fb5b2f..0000000000 --- a/docs/docs/developer-guide/pre-commit.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: default -title: Pre-Commit Usage -parent: Developer Guide -nav_order: 8 ---- - -# Pre-Commit Usage -{: .no_toc } - -How to use pre-commit on the project - -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -### Context - -[Pre-commit](https://pre-commit.com/) is a python package that enables projects to specifies a list of hooks to run before a commit is made (a pre-commit hook). This is really useful to enforce standards or conventions, as it prevents non conformant changes from getting committed. - -On this project, we use pre-commit to automate several checks, including: -- running a code formatting check based on [prettier](https://prettier.io/) -- checking for large files typically not desired to keep in source control -- scanning for secret material, such as AWS keys - -Aside from these checks being run prior to any commit being pushed, they are also run by a GitHub Actions workflow when a pull request is made. - -### Installation - -Good news! If you completed onboarding and ran the [workspace setup]({{ site.baseurl }}{% link docs/onboarding/workspace-setup.md %}) script, pre-commit should already be installed on your machine. - -You can test that it's installed by running `pre-commit -V` in a terminal window. If you get a nominal return including a version number, you're all set. If the pre-commit command is not found, please refer back to the Onboarding / Workspace Setup section of this site. -If pre-commit is not installed it is important to get it installed and setup on your machine. This is a part of the workflow for developing apps in this architecture. Luckily setup is simple. - -### Configuration - -Although pre-commit is installed on your workstation, you must configure pre-commit to run for a given repository before it will begin blocking bad commits. - -This procedure needs to only be run once per repository, or once each time the .pre-commit-config.yaml file is changed in the repository (very infrequently). - -- open a terminal -- install all hooks configured in .pre-commit-config.yaml - ```bash - cd {{ site.repo.name }} - pre-commit install -a - ``` - -That's it -- after running the above commands inside the project repository, pre-comit will run the project's configured checks before any commit. \ No newline at end of file diff --git a/docs/docs/developer-guide/run-commands.md b/docs/docs/developer-guide/run-commands.md deleted file mode 100644 index bc758cbbc8..0000000000 --- a/docs/docs/developer-guide/run-commands.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: default -title: Run Commands -parent: Developer Guide -nav_order: 10 ---- - -# Running Top Level Commands -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -## Overview -The `src/run.ts` file defines many helpful commands you can use to perform helpful tasks for your project. This file utilizes Yargs to provide command line interfaces to several commands for managing a serverless project. - -This code at `src/run.ts` is implementing a command-line interface (CLI) for your project. The CLI accepts various commands to manage and deploy the project. The CLI tool uses the yargs library to parse the command-line arguments. - -The CLI provides the following commands to use like `run [command] [options]` - -## Commands - -`install` installs all service dependencies. -`ui` configures and starts a local React UI against a remote backend. -`deploy` deploys the project. -`test` runs all available tests. -`test-gui` opens the unit-testing GUI for vitest. -`destroy` destroys a stage in AWS. -`connect` prints a connection string that can be run to 'ssh' directly onto the ECS Fargate task. -`deleteTopics` deletes topics from Bigmac which were created by development/ephemeral branches. -`syncSecurityHubFindings` syncs Security Hub findings to GitHub Issues. -`docs` starts the Jekyll documentation site in a Docker container, available on http://localhost:4000. - -## Options -Each command has its own set of options that can be passed in the command line. - -For example, if the command deploy is used, it requires the options stage (type string, demanded option), and service (type string, not demanded option). The behavior of the command is defined by an async function, which will run the installation of all service dependencies and will execute the deployment process through the runner.run_command_and_output function with the command SLS Deploy and the options set in the command line. - -The same approach is used for all other commands. They all start by installing the dependencies of the services, and then perform specific tasks based on the options passed in the command line. - -The docs command starts a Jekyll documentation site in a Docker container. If the stop option is passed as true, it will stop any existing container. Otherwise, it will start a new container and run the documentation site at http://localhost:4000. diff --git a/docs/docs/developer-guide/subscribe-to-alerts.md b/docs/docs/developer-guide/subscribe-to-alerts.md deleted file mode 100644 index 69fc28b716..0000000000 --- a/docs/docs/developer-guide/subscribe-to-alerts.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -layout: default -title: Subscribing to Alerts -parent: Developer Guide -nav_order: 6 ---- - -# Subscribing to Alerts -{: .no_toc } - -How-to to subscribe to alerts from a stage. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -### Subscribing to Alerts - -#### Summary -This project uses SNS for near real time alerting of application health and performance. Subscriptions to this topics are not made automatically, for a few reasons (see alerts service details). This will guide you in how to create a sbuscription. - -#### Prerequisites: -- Completed all [onboarding]({{ site.baseurl }}{% link docs/onboarding/onboarding.md %}) - -#### Procedure -- Go to the AWS Console -- Choose your region in the top right drop down. -- Navigate to SNS -- Click topics (see left hand side hamburger menu) and select your stage's topic -- Click add subscription and follow the prompts. -- If you control the inbox for the subscription you just added, go to the inbox and click through the confirmation email from AWS. -- Repeat these steps for the application's other region. - -#### Notes -- None - diff --git a/docs/docs/developer-guide/unit-testing.md b/docs/docs/developer-guide/unit-testing.md deleted file mode 100644 index 587def96d0..0000000000 --- a/docs/docs/developer-guide/unit-testing.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: default -title: Unit Testing -parent: Developer Guide -nav_order: 11 ---- - -# Unit Testing -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - ---- - -## Overview -Unit testing is done using the vitest framework. - -## Vitest -Vitest is a unit testing framework for testing JavaScript code. It allows you to write tests in a simple and concise manner, and provides tools for running and reporting on the results of those tests. - -## Running Tests -Tests can be run using the top level run commands: -- `run test --stage [stage]` - running all tests -- `run test-gui --stage [stage]` - running all tests displaying results in browser ui diff --git a/docs/docs/developer-guide/update-diagram.md b/docs/docs/developer-guide/update-diagram.md deleted file mode 100644 index d47f0881a0..0000000000 --- a/docs/docs/developer-guide/update-diagram.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: default -title: Updating Diagram -parent: Developer Guide -nav_order: 1 ---- - -# Updating Architecture Diagram -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - -## Purpose -The goal of this document is to guide users on how to update the maco architectural diagram using eraser.io - -## What is eraser.io -Eraser is a tool that enables developers to create docs and diagrams at the speed of thought via a minimal UI, keyboard-driven flows, markdown, and diagram-as-code. To access the editable version of the MAKO use the link provided below -[Architecture Diagram](https://app.eraser.io/workspace/saCiL3CRpdt2oLGOGt6D?elements=UYl7J6SS3K2ovIk2DEydLg) - -## Updating Architectural Diagram -1. Go to the link provided above -2. Make desired changes to the diagram using the tools provided by eraser.io -3. Copy diagram - - Select the entire diagram (highlight all images) - - Right click on any area of the diagram while highlighted - - Select "Copy/Past As" and select "Copy As SVG" -4. Go to docs > assets > diagram-twomac.svg - - Replace the copied item eraser.io with the item in line 4 to 6 diff --git a/docs/docs/developer-metrics.md b/docs/docs/developer-metrics.md deleted file mode 100644 index cb1910e828..0000000000 --- a/docs/docs/developer-metrics.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: default -title: Dev (DORA) Metrics -nav_order: 7 ---- - -# DORA Metrics -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - -

-### [ClICK HERE]({{ site.url }}{{ site.repo.name }}/metrics/dora) to view current DORA metrics for the {{ site.repo.name }} project. -{: .no_toc } -

- - -## Purpose of this view -The goal of the DORA metrics view is to provide a pane of glass view into the effieiency of 'dev' teams along with the quality of code that they produce. These metrics and the questions that they ask is based in large part around DORA. - -## What is DORA -DORA, short for (DevOps Research and Assessment) was a study performed by google into determining a way rate the performance and efficiency. The below article does a great job explaining in detail. -[View Article](https://www.leanix.net/en/wiki/vsm/dora-metrics) - -## How does it work -Linked below is Development Metrics dashboard in question. But in essence it collects a data around deployments, commits, and pull-requests that occur in github and displays it in a useful and easily readable way. diff --git a/docs/docs/onboarding/access-requests.md b/docs/docs/onboarding/access-requests.md deleted file mode 100644 index 49eb04b529..0000000000 --- a/docs/docs/onboarding/access-requests.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -layout: default -title: Access Requests -parent: Onboarding -nav_order: 2 ---- - -# Access Requests -{: .no_toc } - -You'll need access to a few systems to be a fully privileged developer. This section will guide you in making the necessary access requests. -{: .fs-6 .fw-300 } -**Note: Account access should be your first step in onboarding, as the requests can take time to complete.** - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} ---- - -## Git organization - -To be a fully privileged developer on the {{ site.repo.name }} project, you will need access to both the GitHub organization and the repository. - -Access to the GitHub organization is governed by a CMS team and CMS Job Codes. Please follow the [instructions to obtain org access](https://qmacbis.atlassian.net/l/cp/xe4XCoGo). - -## Git Repository - -To be a fully privileged developer on the {{ site.repo.name }} project, you will need access to both the GitHub organization and the repository. - -To be granted access to the repo:, please send an email to {{ site.contact_email }} which includes: - -- Name -- GitHub user id -- Level of access requested (read, write, admin, maintain) -- Reason for access / who you are - -## AWS and Cloud VPN - -The {{ site.repo.name }} project is deployed to a designated set of three AWS accounts. While many workflows can be done without direct AWS Console/CLI access, a fully equipped developer will need AWS access. - -Further, accessing the AWS Console or CLI requires fetching temporary credentials from a service called Kion. This service is behind the CMS Cloud VPN. - -So, to have full AWS access, you will need two things: access to the Cloud VPN so you can hit Kion, and then permissions for the project's specific AWS accounts. CMS requests to be granted access to these two systems can be made together, or seperately. - -Please [follow this how-to guide](https://qmacbis.atlassian.net/l/cp/yY5s5is2) to obtain access to AWS and the Cloud VPN. - -## Zscaler VPN - -This project communicates with Seatool. Sometimes, during development, access to the Seatool frontend is helpful. To gain access to it, you must first get access to the Zscaler VPN. - -Please follow these [instructions to gain access to Zscaler](https://qmacbis.atlassian.net/wiki/spaces/DE/pages/3180560407/How+to+get+access+to+CMS+Zscaler+VPN+Access). - -## Snyk - -Snyk is a software tool that specializes in identifying and resolving security vulnerabilities in code dependencies and performing static code analysis. It scans project dependencies for known security issues and provides recommendations for fixing them, enhancing code security. - -CMS has a Snyk installation that our project ties into. It can be found [https://snyk.cms.gov/](here). Access is governed by a CMS job code, as well as Snyk permissions. - -Please follow these steps to gain access to Snyk: -1. Request / Confirm EUA job code: ENT_APPSEC_TOOLS: Access to Enterprise Application Security Tools: Snyk -2. Login to Enterprise User Administration (EUA) -3. Select View My Identity -4. Select the "Job Codes" tab -5. Review your current Job Codes. Do you have ENT_APPSEC_TOOLS? -6. If no, request job code ENT_APPSEC_TOOLS -- On the “Task” sidebar select “Modify My Job Codes -- Be sure to select the “*Confirmation (Required) check box -- Select Next -- At the bottom left of the page select “Add a Job Code” -- Enter the job code you want to add, In this case ENT_APPSEC_TOOLS and select “Search” -- Check the select box and click the “Select” bottom. -- Click “Next” -- Enter a “Justification Reason” and select “Finish” -- Example: I am a CMS contractor, requesting Access to Enterprise Application Security Tools: Snyk order to support development and maintenance of the suite of MACPRO systems supported by Fearless and it’s sub-contractors under Primary Contract Number: GS-35F-115GA:75FCMC22F0093: -- Now wait for the request to be approved. You need ENT_APPSEC_TOOLS before the cloud team will setup your access. -7. Let me know when each user has confirmed that you have the ENT_APPSEC_TOOLS the Job Code and I will send a Snyk invite the email address associated with their EUA ID. - -## Code Climate - -We use [Code Climate](https://codeclimate.com/) to monitor project quality. This includes running maintainability checks for Pull Requests, which flags code that doesn't meet best practices. Checks include function length, file length, cognitive complexity, and duplication. - -Code Climate is a completely external tool which is free to use. You may go to Code Climate and create a new account. We recommend you sign up with GitHub, for convenience, so you won't need to maintain a separate username and password. - -Once you have an account, you may view any repositories for which you have access. For private repositories, you will not be able to view the repository in Code Climate until you have Git repository write access (see above). \ No newline at end of file diff --git a/docs/docs/onboarding/communication.md b/docs/docs/onboarding/communication.md deleted file mode 100644 index 7051dc8522..0000000000 --- a/docs/docs/onboarding/communication.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -layout: default -title: Communication -parent: Onboarding -nav_order: 3 ---- - -# Communication -{: .no_toc } - -Here's how our team communicates. -{: .fs-6 .fw-300 } -**Note: Account access should be your first step in onboarding, as the requests can take time to complete.** - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} ---- - -## Recurring Meetings - -Here's a list of recurring meetings you might need. If you need or want an invite, reach out on [Slack]({{ site.slack.channel_url}} ). - -| Meeting | Day(s) | Time | Link | -| ------- | ------ | ---- | ---- | -|{% for element in site.meetings %} {{ element.name }} | {{ element.days }} |{{ element.time }} | {{ element.link }} | -{% endfor %} - -## Slack - -We love Slack. Our primary means of synchronous and/or ad hoc communication is our project [Slack channel]({{ site.slack.channel_url }}). All developers and stakeholders should join the channel, and feel free to put any kind of project related information in it. Questions, comments, discussions about failing builds, "is anyone available to help me?", "i'm going to be out for a few hours", "check out this cool new thing I found", "good morning everyone", etc. can all go in the Slack channel. - -Please note that comments related to Pull Requests and Issues are best made on those objects. For example: putting a comment about a Pull Request in Slack is generally inappropriate, as the Pull Request is then not a complete record of what occurred. - -However, and please remember this: do what you feel is best. Discretion is what allows us to move fast, so don't be afraid to break the guidelines. - -## Jira - -We use Jira to document work items. This should be the place where ideas are written down, where acceptance criteria is developed, and where questions/comments/concerns pertaining to the work should take place. Specifically, you probably don't want to comment on a work item or its content in Slack, as it's very valuable to have the entire development process on record in the Jira Task/Story. So, put work item related content in the Task as much as possible. - -## GitHub Pull Requests (PRs) - -GitHub Pull Requests are the primary vehicle to propose code changes to {{ site.repo.name }}. A PR is always used to ship code to the main branch, with very few exceptions. - -This project has PR templates which will be used automatically when you create a PR. While the template sets up a PR with the fields that are typically required, you need to add the content to the various sections. - -When authoring a PR, you typically want to be as descriptive as possible. The goal is to write a PR as a complete description of the changeset, so that the PR and any underlying Issue can completely communicate the changes being made to someone otherwise unfamiliar with the work. We want to be able to look back at the PR in a month, six months, or years on, and be able to fully understand what was changed and why. Your audience for PRs are your peers, non technical stakeholders, and your future self. High quality PRs are a hallmark of successful and maintainable projects; don't go short on them. - -## Email - -We don't like email. However, it is used for access requests, onboarding, communication with external entities, and/or for any other communication that's difficult to facilitate another way. - -Don't hesitate to send an email if there's not a better way (do what you think is best), but avoid it by rule and use it by exception. - diff --git a/docs/docs/onboarding/onboarding.md b/docs/docs/onboarding/onboarding.md deleted file mode 100644 index 5b29e59ddb..0000000000 --- a/docs/docs/onboarding/onboarding.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -layout: default -title: Onboarding -nav_order: 3 -has_children: true -permalink: docs/onboarding ---- - -# Onboarding -{: .no_toc } - -Welcome to the team! -{: .fs-6 .fw-300 } - -This section exists to be your guide through the {{ site.repo.name }} project onboarding process. We've done our best to make this as linear and painless as possible... let us know how we did! If you find any gaps in the documentation, inaccurate information, or need help, please reach out via [Slack]({{ site.slack.channel_url }}) or via email to {{ site.contact_email }}. diff --git a/docs/docs/onboarding/team.md b/docs/docs/onboarding/team.md deleted file mode 100644 index 7e40f590d8..0000000000 --- a/docs/docs/onboarding/team.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -layout: default -title: Team Introduction -parent: Onboarding -nav_order: 1 ---- - -# Team Introduction -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -### Meet the team - -A list of a few important roles and people on the project (not comprehensive!): - -| Role | Description | Team Member | Email | -| ---- | ----------- | ----------- | ----- | -|{% for element in site.team.members %} {{ element.role }} | {{ element.description }} | {{ element.name }} | {{ element.email }} | -{% endfor %} - - -### Core Work Hours - -Core Team work hours are {{ site.team.core_hours }}. - -We strive to do most work asynchronously, as we are spread across timezones and have others demands on our time. Using Pull Request reviews and comments, Slack, and email are examples of ways we try to keep things async. However, we strive to be available online for meetings and pair programming during this time window. \ No newline at end of file diff --git a/docs/docs/onboarding/workspace-setup.md b/docs/docs/onboarding/workspace-setup.md deleted file mode 100644 index 4d440ba6ad..0000000000 --- a/docs/docs/onboarding/workspace-setup.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: default -title: Workspace Setup -parent: Onboarding -nav_order: 4 ---- - -# Workspace Setup -{: .no_toc } - -Before you begin development, it's important to configure your workstation properly. This section will give you an overview of what tools are installed and get you bootstrapped. -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} ---- - -## Development Tool list -This is a static list of tools that should be pre-installed to support all Developer Guide. Please understand that the installation of most of these tools is automated, which will be discussed in the next section. This serves as a general overview of what will be installed. - -| Tool | Version | Required | -| --------------------------------------------------------------------------------- | --------- | -------- | -| [MacOS](https://www.apple.com/macos/monterey/) | 10.15+ | Yes | -| [Rosetta 2 (Apple Silicon only)](https://support.apple.com/en-us/HT211861) | 2 | Yes | -| [XCode Command Line Tools](https://mac.install.guide/commandlinetools/index.html) | 2392 | Yes | -| [NodeJS](https://nodejs.org/en/) | 14.x | Yes | -| [Node Version Manager (NVM)](https://github.com/nvm-sh/nvm) | 0.39.1 | Yes | -| [Bun](https://bun.sh/) | 1.1.20 | Yes | -| [Direnv](https://direnv/) | 2.31.0 | Yes | -| [AWS CLI ](https://aws.amazon.com/cli/) | 2.x | Yes | -| [AWS CLI Sessions Manager Plugin][ssmpluginlink] | 1.2.312.0 | Yes | -| [jq](https://stedolan.github.io/jq/) | jq-1.6 | Yes | -| [awslogs](https://github.com/jorgebastida/awslogs) | 1.2.312.0 | Optional | -| [Git](https://git-scm.com/) | 2.x | Yes | - -[ssmpluginlink]: https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html - - -## Install Rosetta 2 (Apple Silicon only) - -If you're on a newer model Mac with an Apple ARM based chip (M1 series), you need to install Rosetta 2. - -Follow this [Apple Support guide](https://support.apple.com/en-us/HT211861) to install Rosetta 2. - -## Install XCode Command Line Tools - -Follow the [installation guide](https://mac.install.guide/commandlinetools/index.html). - -## Run the setup.sh script - -Once you reach this step, the remainder of the tools you'll need can be installed with a simple script. - -- Download and save the [setup.sh script](../../../assets/setup.sh) somewhere accessible on your computer. Your Downloads folder, which is probably your default download location, is fine. -- Open a terminal -- Run the setup.sh script - ``` - sh ~/Downloads/setup.sh - ``` - Please note that you may be prompted to input your OS user's password, as some installation steps require higher priveleges. - -After successfully running the setup script, close all open terminals. The script adds PATH modifications to your .zshrc file, and that file will only take affect when starting a new terminal session. To ensure you immediately have the PATH modifications available, it's easiest to simply close all terminal windows now. You may reopen the terminal at any time afterwards. - -## Clone the repository - -Now that you have all prerequisites installed, it's time to get the code base. This will require your git repo access request to have been completed. - -- Configure your GitHub user with an SSH key, and add it to your ssh-agent. [Help can be found here](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent). Using https and personal access tokens, instead of SSH and a key, is possible but discouraged. -- Clone the repository - ``` - git clone git@github.com:{{ site.repo.org }}/{{ site.repo.name }}.git - ``` - -## Configure AWS CLI - -This step requires that your request for AWS access has been completed. If it hasn't, you'll need to wait, then return here. - -- Login to the AWS Console. -- Create an AWS Access Key Id and Secret Access Key for your IAM user. Detailed instructions are [available](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey). -- Configure the AWS CLI with valid credentials. Background and instructions can be found [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html). Running `aws configure` and following the prompts should suffice, though. - -## Now what? - -Nice job! If you've successfully stepped through this document, you should be entirely ready to start active development on the {{ site.repo.name }} project. - -If you've had errors along the way, that's OK! We're here to help. If you've successfully joined our Slack channel, feel free to post there. Else, please send an email to mdial@collabralink.com explaining the issue. We will get back to you ASAP. diff --git a/docs/docs/overview.md b/docs/docs/overview.md deleted file mode 100644 index de9b3c22ea..0000000000 --- a/docs/docs/overview.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: default -title: Overview -nav_order: 2 ---- - -# Overview -{: .no_toc } - -The 10,000ft view -{: .fs-6 .fw-300 } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -## Overview - -The {{ site.repo.name }} project is a redesign of MACPRO Onemac. The mission to be a modern submission and review portal for select CMS data remains the same, but the architecture is different in some important ways. - -## Architecture - -A diagram is often the best way to communicate the architecture: - -![diagram](../assets/diagram.svg) - - -## Development Metrics (DORA) - -We programmatically publish a set of Development metrics that align to the DevOps Research and Assesment (DORA) standards. Those metrics can be viewed [here]({{ site.url }}{{ site.repo.name }}/metrics/dora). - -## AWS Resources - -You can view and download a list of all aws resources this project uses for higher environments [here]({{ site.url }}{{ site.repo.name }}/metrics/aws). \ No newline at end of file diff --git a/docs/docs/services/alerts.md b/docs/docs/services/alerts.md deleted file mode 100644 index b051cec739..0000000000 --- a/docs/docs/services/alerts.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: default -title: alerts -parent: Services ---- - -# alerts -{: .no_toc } - -#### Summary - -The alerts service deploys a Simple Notification Service (SNS) topic to REGION_A. This topic can be leveraged by any other service for sending alerts. - -#### Detail - -- To subscribe an email, phone number, or something else to the topic, find the SNS topic using the AWS Console and add the subscription manually. -- No SNS subscriptions are made by the deployment process. The topic is created, and several other services are configured to publish notifications to the topic, but the topic itself is not automatically configured to fan out any notifications. Here's why: - - Since dev environments may receive many notifications due to failures related to development, and since those notifications can be noisy, we likely never want to automatically subscribe to dev environments' SNS topics. - - We likely only want to subscribe to notifications for higher/long running environments like main, val, and production. - - Manually adding the subscription to higher environments was judged to be low effort, as it's a one-time operation. - - After adding an email as a subscriber to SNS, the email must be confirmed by clicking a link in a confirmation email. This added to the decision to handle subscriptions manually, as a human would need to verify the email manually even if the subscription was made automatically. diff --git a/docs/docs/services/api.md b/docs/docs/services/api.md deleted file mode 100644 index bd03eb14fa..0000000000 --- a/docs/docs/services/api.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: default -title: api -parent: Services ---- - -# api -{: .no_toc } - -## Summary - -The api service deploys a lambda-backed API Gateway that is used by the frontend to interact with the data layer. Access to any of its endpoints is guarded at a high level by AWS Cognito, ensuring only authenticated users may reach it. The lambda functions that back each endpoint enforce further fine-grain access according to business rules. - - -## Detail - -The largest component of the api service is the API Gateway itself. This is a standard deployment of a regional, REST API Gateway. We do not apply custom certificates or DNS names to the api gateway endpoint (yet); instead, our application uses the amazon generated SSL endpoint. - -There are four endpoints on the api. Each is guarded by AWS IAM, meaning that while the API Gateway is publicly available, the API will not forward your request to the backing lambda unless you provide valid credentials obtained through AWS Cognito. This way, only users with an account that we can authenticate may successfully call endpoints. The four endpoints are: -- /search (POST): This endpoint accepts search queries from clients in the form of OpenSearch Query DSL queries. Once the query is received, the lambda adds extra query filters to ensure fine grain auth. This works by looking up the user making the call in Cognito, determining what type of user (cms or state) is making the call, determining what states that user has access to (if appropriate), and modifying the query in a way that will only return results for those states. By design, the only thing the search endpoint adds is related to authentication; the rest of the query building is left to the frontend for faster and more flexible development. -- /item (POST): The item endpoint is used to fetch details for exactly one record. While you can form a query to do this and use the search endpoint, the item endpoint is for convenience. Simply make a post call containing the ID of the desired record to the item endpoint, and the record will be returned. Note that fine grain auth is still enforced in an identical way to search, whereby you will only obtain results for that ID if you should have access to that ID. -- /getAttachmentUrl (POST): This endpoint is used to generate a presigned url for direct client downloading of S3 data, enforcing fine grain auth along the way. This is how we securely allow download of submission attachment data. From the details page, a user may click a file to download. Once clicked, their client makes a post to /getAttachmentUrl with the attachment metadata. The lambda function determines if the caller should or should not have access based on identical logic as the other endpoints (the UI would not display something they cannot download, but this guards against bad actors). If access is allowed, the lambda function generates a presigned url good for 60 seconds and returns it to the client browser, at which point files are downloaded automatically. -- /allForms (GET): This endpoint serves GET requests and will return a list off all available webforms and their associated version. the result will look like: { ABP1: [ '1', '2' ], ABP2: [ '1' ] } -- /forms (GET): This endpoint function serves as the backend for handling forms and their associated data. This function provides various features, including retrieving form data, validating access, and serving the requested form content. The request to this endpoint must include a formId in the request body. Optionally, you can include a formVersion parameter. If you access this endpoint with formId without specifying formVersion, it will return the latest version. Form schemas are stored in a Lambda layer. Each form is organized in its directory, and each version is stored within that directory. The Lambda layer is located in the "opt" directory when deployed to aws. To access a specific version of a form with a formId, use the following URL structure: /opt/${formId}/v${formVersion}.js. The form schemas are versioned and stored in Git under the "api/layers" directory. - -All endpoints and backing functions interact with the OpenSearch data layer. As such, and because OpenSearch is deployed within a VPC, all lambda functions of the api service are VPC based. The functions share a security group that allows outbound traffic. - -All function share an IAM role. This is for convenicence; we can do one role per function if we find that valuable. The permissions include: -- OpenSearch permissions to allow access to the data layer -- Cognito permissions to look up user attributes; allows for enforcement of fine grain auth. -- AssumeRole permissions for a very specific cross account role, which is required to generate the presigned urls for the legacy OneMac data. - diff --git a/docs/docs/services/auth.md b/docs/docs/services/auth.md deleted file mode 100644 index 19509f323e..0000000000 --- a/docs/docs/services/auth.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: default -title: auth -parent: Services ---- - -# auth -{: .no_toc } - -## Summary -The auth service builds the infrastructure for our authentication and authorization solution: Amazon Cognito. A user pool and identity pool is deployed, and may conditionally be pointed to IDM (external identity provider). - -## Detail -The core of the api service is a cognito user pool and identity pool, which work together to provide an auth solution: -- user pool: this is the user directory, where all active users and their attributes are stored. - - This is where we specify the user attribute schema, informed by but not beholden to IDM. - - The attribute schema is difficiult to update, and often requires deleting the user pool. This is acceptable for two reasons. One, updating the attribute schema would be a rare event. Two, since in higher environments all users are federated, the user pool itself holds no unique data; as such, it is safe to delete and simply rebuild without having data loss. -- identity pool: this is associated with the user pool, and allows us to grant certain AWS permissions to authenticated and/or unauthenticated entities. - - authenticated users may assume a role that gives them permissions to invoke the api gateway, as well as see information about their own cognito user. - - unauthenticated user may assume a role that gives them no permissions. - -In the near future, higher environments will configure IDM as an external identity provider. Ephemeral/dev environments will continue to use only the cognito user pool. \ No newline at end of file diff --git a/docs/docs/services/dashboard.md b/docs/docs/services/dashboard.md deleted file mode 100644 index 26e7c7363c..0000000000 --- a/docs/docs/services/dashboard.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: default -title: dashboard -parent: Services ---- - -# dashboard -{: .no_toc } - -## Why do I need this? -Part of any good project is a way to determine how well it is working. The purpose of a `CloudWatch Dashboard` is to determine the performance, health, and a variety of other aspects that factor into the product being delivered. What we have done here is provided an easy to use solution that will make creating a dashboard easy and deploying it even easier. - -## Quick Disclaimer -In order to add the dashboard to existing projects it is important to note that is relies on consistant namespacing across aws services. It must be able to distinguish things such as the project and branch name for example. - -## Getting Started -In order to use the CloudWatch Dashboard you must bring over the dashboard folder which is located in the `src/services/` directory of this repo. Where this folder gets added is entirely dependant upon the structure of the project it's being added to, but the good news is that this service has no dependencies on other services (meaning it is standalone). - -## Making edits to the Dashboard -Once the dashboard is deployed to the AWS account it can be found in the CloudWatch Dashboards section by the name of `${stage-name}-dashboard`. - -Edits can be made to this dashboard and when edits are complete simply save the dashboard and then click on the `generate template` button. The contents in here are what the `templateDashboard.txt` file should consist of. A simple copy, paste, and commit later and the changes are now ready to be deployed to higher environments. diff --git a/docs/docs/services/data.md b/docs/docs/services/data.md deleted file mode 100644 index 5f4c705397..0000000000 --- a/docs/docs/services/data.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -layout: default -title: data -parent: Services ---- - -# data -{: .no_toc } - -## Summary -The data service deploys our OpenSearch data layer and supporting infrastructure. - -## Detail -OpenSearch, Amazon's managed Elasticsearch offering, was selected as the data layer tech. \ No newline at end of file diff --git a/docs/docs/services/email.md b/docs/docs/services/email.md deleted file mode 100644 index ac3bbabdfc..0000000000 --- a/docs/docs/services/email.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: default -title: email -parent: Services ---- - -# email -{: .no_toc } - -## Summary -The email service deploys the lambdas, SNS topics, and Configuration Sets needed to send email. - -## Detail -AWS SES is an account-wide service for basic sending and receiving of email. By creating lambdas to build the emails and sending the email with a branch-specific configuration set, we can follow the events of email sending and take action based on those events. - -### Secrets Manager -The workflow will not successfully deploy unless the emailAddressLookup object is defined: - -Named {project}/default/emailAddressLookup or {project}/{stage}/emailAddressLookup - { - "sourceEmail":"\"CMS MACPro no-reply\" ", - "osgEmail":"\"OSG\" ", - "chipInbox":"\"CHIP Inbox\" ", - "chipCcList":"\"CHIP CC 1\" ;\"CHIP CC 2\" ", - "dpoEmail":"\"DPO Action\" ", - "dmcoEmail":"\"DMCO Action\" ", - "dhcbsooEmail":"\"DHCBSOO Action\" " - } - -These values are set during deployment as environment variables on the lambda. You can edit these values in the AWS Console on the Lambda configuration tab. - -LAUCH NOTE!!! The defined email addresses have been stored as om/default/emailAddressLookup in the production account, with om/production/emailAddressLookup overwriting those email addresses with the test email addresses. Delete the om/production/emailAddressLookup before the real launch deploy (you can also edit the environment variables after the lambda is built). - -### Test accounts -There are gmail accounts created to facilitate email testing. Please contact a MACPro team member for access to these inboxes. At this time, there is only one available email inbox. -- mako.stateuser@gmail.com - a state user account - does have an associated OneMAC login - -### Templates -The email services uses the serverless-ses-template plugin to manage the email templates being used for each stage. To edit the templates, edit index.js in ./ses-email-templates. Each template configuration object requires: -- name: the template name (note, the stage name is appended to this during deployment so branch templates remain unique to that stage). At this time, the naming standard for email templates is based on the event details. Specifically, the action and the authority values from the decoded event. If action is not included in the event data, "new-submission" is assumed. -- subject: the subject line of the email, may contain replacement values using {{name}}. -- html: the email body in html, may contain replacement values using {{name}}. -- text: the email body in text, may contain replacement values using {{name}}. - -## Email Sending Service with AWS CDK - -This guide provides an overview and implementation of a robust email sending service using AWS Cloud Development Kit (CDK). The service includes features such as dedicated IP pools, configuration sets, verified email identities, and monitoring through SNS topics. diff --git a/docs/docs/services/services.md b/docs/docs/services/services.md deleted file mode 100644 index fc33657339..0000000000 --- a/docs/docs/services/services.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -layout: default -title: Services -nav_order: 3 -has_children: true -permalink: docs/services ---- - -# Services -{: .no_toc } - -Details on each Serverless service -{: .fs-6 .fw-300 } - -The {{ site.repo.name }} project is a [serverless monorepo](https://serverless-stack.com/chapters/organizing-serverless-projects.html). It is, for the most part, a collection of standalone Serverless Framework micro services bound together in a repository. Loose coupling of the micro services is facilitated using one or several tools, which include CloudFormation outputs, AWS Systems Manager Parameter Store paramters, and AWS Secrets Manager stores. This section will describe each service in a high level of detail. diff --git a/docs/docs/services/ui-infra.md b/docs/docs/services/ui-infra.md deleted file mode 100644 index f429853627..0000000000 --- a/docs/docs/services/ui-infra.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: default -title: ui-infra -parent: Services ---- - -# UI Infra -{: .no_toc } - -## Summary -This service provides the appropriate infrastructure for the UI application running on AWS. It creates several resources including an S3 bucket, a bucket policy, a logging bucket, a logging bucket policy, and an IAM role with permissions. - - -## Details - -- AWS IAM role with permissions for CloudWatch logs and an IAM boundary policy. -- Serverless plugins to help with deploying and managing the infrastructure. -- Configuration settings for different stages of the infrastructure, including DNS record, CloudFront domain name, and certificates. -- A set of resources to be created, including S3 buckets for hosting the UI, logging, and their policies. - -## Resources - -- An S3 bucket with server-side encryption and the ability to serve static web content. -- A bucket policy that allows access to the bucket from an AWS CloudFront distribution using an Origin Access Identity (OAI). -- An S3 bucket for CloudFront access logs with server-side encryption and an access policy that allows AWS root account to write logs. -- A conditional statement for DNS record creation and a conditional statement for CloudFront distribution creation. diff --git a/docs/docs/services/ui.md b/docs/docs/services/ui.md deleted file mode 100644 index de40993a1a..0000000000 --- a/docs/docs/services/ui.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: default -title: ui -parent: Services ---- - -# UI -{: .no_toc } - -## Overview -This service deploys a static web application to an S3 bucket with a CloudFront distribution in front of it for CDN caching and performance optimization. The template uses the serverless framework and includes several plugins to help with deployment and configuration. - -## Configuration - -The custom section defines some custom variables, including the project name, stage, region, and CloudFormation termination protection for specific stages. The s3Sync section defines the S3 bucket to which the files will be synced, the local directory where the files will be found, and whether to delete removed files. - -The cloudfrontInvalidate section invalidates the CloudFront distribution cache by specifying the distribution ID and the items to invalidate. The scripts section defines a script to set environment variables during deployment, which are used to specify the API region and URL. - -The provider section configures the runtime environment for the Lambda functions, the AWS region, and stack tags. It does not include any IAM configuration since no Lambda functions are defined. - -This template is mainly focused on deploying the static web application to S3 and configuring the CloudFront distribution to serve the content. The environment variables set in the scripts section are used by the application to connect to the backend API. - -## Scripts -There are three npm scripts that are defined in the package.json file of a project. These scripts are used to automate certain development tasks related to the project. - -1. `dev`: This script runs the Vite development server. Vite is a build tool that enables fast development by providing a development server that reloads the browser quickly whenever changes are made to the code. When the dev script is run, Vite starts the development server and serves the project files on a local web server. The output of this script will typically be a URL that can be opened in a web browser to access the development server. - -1. `build`: This script builds the project for production. This script first runs the TypeScript compiler (tsc) to compile the TypeScript code to JavaScript. After that, the Vite build tool is run to bundle the code and assets for production. The output of this script will typically be a set of static files that can be deployed to a web server. - -1. `preview`: This script starts a Vite server that serves the production build of the project on a local web server. This is useful for testing the production build locally before deploying it to a web server. When this script is run, Vite starts the production server and serves the project files on a local web server. The output of this script will typically be a URL that can be opened in a web browser to access the production server. \ No newline at end of file diff --git a/docs/docs/team-norms/code-style.md b/docs/docs/team-norms/code-style.md deleted file mode 100644 index fca3a870c4..0000000000 --- a/docs/docs/team-norms/code-style.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -layout: default -title: Code Style Cheatsheet -parent: Team Norms -nav_order: 2 ---- - -# Code Style - -### What is it? - -A set of rules or guidelines used when writing the source code for a computer program. - -### Why is it? - -It keeps the code looking neat and tidy, so anyone on the team can jump in and not -get lost in a jungle of curly braces and indentation levels. It's like everyone speaking -the same slang in a super cool secret club. Plus, it saves time from arguing over trivial -stuff, like whether tabs are better than spaces, so there's more time for the fun -stuff—coding and creating! - -# OneMAC Style Norms - -## Cheatsheet - -TL;DR? No worries, here's a cheatsheet of the concepts outlined below: - -- [DO NOT destructure](#object-access) so we maintain object context for methods and properties used in code - -### Object Access - -When integrating with complex objects, consider maintaining the object's integrity rather -than opting for destructuring. This approach ensures that the object's context is -preserved, enhancing readability and maintainability. - -#### Nomenclature simplification - -For instance, rather than breaking -down the object into individual variables, which can lead to verbose and confusing naming -conventions, maintain the object as a whole. - -```typescript jsx -const { - setModalOpen, - setContent: setModalContent, - setOnAccept: setModalOnAccept, -} = useModalContext(); -const { - setContent: setBannerContent, - setBannerShow, - setBannerDisplayOn, -} = useAlertContext(); - -// vs - -const modal = useModalContext(); -const alert = useAlertContext(); -``` - -#### Usage implication - -This method simplifies reference to its properties and methods, providing a clearer and -more direct understanding of its usage within the code. This strategy is particularly -beneficial in scenarios where the object's structure and context significantly contribute -to its functionality and meaning in the application. - -```typescript jsx -
{ - try { - await submit({ - //... - }); - alert.setContent({ - header: "RAI response submitted", - body: `The RAI response for ${item._source.id} has been submitted.`, - }); - alert.setBannerShow(true); - alert.setBannerDisplayOn("/dashboard"); - navigate({ path: "/dashboard" }); - } catch (e) { - //... - } - })} -> -``` \ No newline at end of file diff --git a/docs/docs/team-norms/jira-workflow.md b/docs/docs/team-norms/jira-workflow.md deleted file mode 100644 index 1afd24acb6..0000000000 --- a/docs/docs/team-norms/jira-workflow.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -layout: default -title: JIRA Workflow -parent: Team Norms -nav_order: 2 ---- - -# JIRA Workflow - -## Introduction - -This document outlines our team's process for managing tasks using the JIRA Kanban board. It's designed to provide clarity and consistency on how we track and progress through work items. - -## Kanban Columns - -### Backlog - -- **Population**: The backlog is populated with new tasks during the planning phase. -- **Prioritization**: Tasks are prioritized based on urgency and importance. - -### Ready - -- **Preparation**: Tasks are moved to "Ready" once they are clearly defined and ready for development. -- **Final Checks**: Ensure that all necessary information and acceptance criteria are present. Story points should be added prior to moving to Ready. - -### In Progress - -- **Active Development**: When a task is actively being worked on, it is moved to "In Progress". -- **Daily Updates**: Developers should provide daily updates or comments on the task to indicate progress. - -### In Review - -- **Code Review**: Tasks are moved here when the development is complete and they are awaiting code review. -- **Peer Feedback**: Team members provide feedback and approval. If feedback requires a significant modification or rewrite to the code, the ticket should be moved back to In Progress. - -### In Testing - -- **Quality Assurance**: Tasks in this column are undergoing thorough testing by the QA team. -- **Env URL**: When a developer moves a ticket into In Testing, the dev should make a comment on the ticket that includes the deployed environment's URL, as well as probably tagging the QA team for convenience. -- **Hands Off**: Developers should not push code that updates environments for work that is In Testing, without coordinating with the QA team. This is to prevent deployments interfering with the QA process. -- **Bug Reporting**: Any issues discovered during testing are reported and linked to the task. -- **Failures**: If a ticket fails QA for reasons that should not be addressed separately (like a bug), the QA team will move the ticket back to In Review. - -### Ready for Merge - -- **Final Approval**: Once testing is complete and the task has passed all checks, it moves to "Ready for Merge". -- **Merge Preparation**: The task is prepared for merging into the main codebase. - -### In Pipeline - -- **Deployment**: Tasks here have been merged to master, and are in the process of being verified on master by the QA team. -- **Monitoring**: Close monitoring of the feature in the live environment for any immediate issues. - -### Done - -- **Completion**: Tasks are moved to "Done" when they are merged to master and verified on master, if applicable. -- **Review**: The team may review completed tasks to ensure all objectives were met. -- **Demo Coordination**: If the completed work is going to be demoed, coordinate a time with Product to relay the context of the feature and how to demonstrate it. - -## Sprint Tracking - -### Current and Upcoming Sprints - -- **Active Sprint**: Tasks currently being worked on are tracked under the active sprint, e.g., "Sprint 2.4". -- **Future Sprints**: Upcoming work items are assigned to future sprints, and periodically rescheduled during refinement as events unfold. - -### Sprint Review - -- **Regular Reviews**: At the end of each sprint, the team reviews progress and adjusts future plans accordingly. -- **Continuous Improvement**: Lessons learned are discussed and processes are adjusted to improve future sprints. - -## Responsibility and Accountability - -- **Ownership**: Team members take ownership of tasks they are working on and update their status accordingly. -- **Collaboration**: The entire team collaborates to ensure tasks move smoothly through the workflow. - -By adhering to this JIRA workflow, we aim to maintain a high level of organization and efficiency within our development process. \ No newline at end of file diff --git a/docs/docs/team-norms/pull-requests.md b/docs/docs/team-norms/pull-requests.md deleted file mode 100644 index 4a4d5a3884..0000000000 --- a/docs/docs/team-norms/pull-requests.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -layout: default -title: Pull Requests -parent: Team Norms -nav_order: 1 ---- - -# Pull Requests - -## Introduction - -Pull Requests (PRs) are an essential part of our software development process, enabling code review, discussion, and integration. This document outlines our team norms regarding PRs to ensure a smooth, efficient, and collaborative workflow. - -Above all else, please remember: your discretion and judgement supersedes this document. If you're creating or reviewing a Pull Request that needs to break any of these norms for good reason, that's OK. This document is the rule; exceptions are expected. - -## Creating Pull Requests - -### Content and Format - -- **Title**: The title of your PR should be carefully considered. It should be descriptive enough to convey the essence of the changes but concise enough to be easily readable. Think of the title as a brief summary that helps others quickly understand the purpose of the PR. -- **Follow the Template**: We use a Pull Request template to standardize and streamline our PR descriptions. It is expected that all PRs will adhere to this template. - -### Small, Focused Changes - -- **Scope**: Keep PRs focused on a single task or issue to simplify review and discussion. While most pull requests will each address a single ticket, it's OK to handle multiple Jira tickets in one PR when it makes sense. -- **Size**: Aim for small, manageable PRs. Large PRs should be broken down into smaller parts if possible. -- **Formatters**: Be conscious of formatting updates that your IDE may make automatically, or that you may make along the way. Sometimes small, non-functional code changes can clutter a pull request. - -## Reviewing Pull Requests - -### Timeliness - -- **Prompt Reviews**: Team members are expected to review PRs in a timely manner, typically next day or sooner. - -### Constructive Feedback - -- **Respectful Communication**: Provide constructive, respectful feedback. -- **Specific Comments**: Use specific comments to point out issues, suggest improvements, or ask clarifying questions. - -### Testing - -- **Test as Appropriate**: The burden of full end to end testing lies with the author, our automated testing frameworks, and any manual QA process. However, reviewers should test the environment when they think it necessary, perhaps when they've thought of an edge case that might not be covered. -- **Explain your testing**: The PR approach should typically include some detail around how manual tests were performed. This helps greatly in allowing reviewers to sign off without needing to test individually. - -## Merging Pull Requests - -### Passing Checks - -- **CI/CD**: Ensure all continuous integration and deployment checks have passed. -- **Code Quality**: Code should meet our standards for quality and maintainability. - -### Approval - -- **Minimum Approvals**: PRs require a minimum number of approvals (1) from the team members before merging. -- **Outstanding Comments**: If there are comments that ask for action or consideration to be made by the author, please address them prior to merge, regardless if you have approval. - -### Merging -- **Author Merges**: After receiving necessary approvals, the PR author is responsible for merging the code. -- **Squashing Protocol**: When merging into the master branch, always squash and merge. When merging into val and production, create a merge commit. -- **Commit Messages**: We use semantic release to automatically release our product. Semantic Release looks for commit messages with special [commit message syntax](https://semantic-release.gitbook.io/semantic-release/#commit-message-format). Please follow this syntax when crafting your commit message. Note: GitHub will use your PR title as the default commit message when squashing; so, it's recommended to set your PR title equal to the semantic release commit message appropriate for your changeset. - -## Responsibility and Accountability - -- **Ownership**: The author is responsible for addressing feedback and ensuring the PR is ready for merging. -- **Collaboration**: All team members share the responsibility for maintaining a high standard of code through thoughtful PR reviews. diff --git a/docs/docs/team-norms/team-norms.md b/docs/docs/team-norms/team-norms.md deleted file mode 100644 index d330c81557..0000000000 --- a/docs/docs/team-norms/team-norms.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -layout: default -title: Team Norms -nav_order: 3 -has_children: true -permalink: docs/team-norms ---- - -# Team Norms -{: .no_toc } - -What's expected and what you can expect -{: .fs-6 .fw-300 } - -wip diff --git a/docs/docs/webforms.md b/docs/docs/webforms.md deleted file mode 100644 index 33c001a832..0000000000 --- a/docs/docs/webforms.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -layout: default -title: Webforms -nav_order: 8 ---- - -# Webforms -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -- TOC -{:toc} - -
-### [ClICK HERE]({{ site.url }}{{ site.repo.name }}/metrics/webforms) to view documentation for individual webforms for the {{ site.repo.name }} project. -{: .no_toc } -
- - -## Purpose of this view -The goal of the webforms view is to be able to view all possible form fields as they relate to the data collected. We try to associate the "name" of the field, which is the key of the data as it is collected and stored, to the prompt and label of the question the user is presented with. - -## What are onemac webforms -These webforms are a replacement for the pdf form uploads previously used in mmdl, etc. These are dynamicly generated forms from a single document that represent the shape of a form and version. There three pieces to this puzzle. -1. The form schema itself. this is the shape of a form that is delivered by the /forms endpoint to the frontend ui. -1. The form genereator. This is a collection of ui react components and `react-hook-form` methods that generate the ui from the form schema and render and validate the form presented to the user. -1. The data. The data collected is added along with its metadata to its parent record. This data can be used to view/approve/edit the form later by admins or other users with appropriate permissions. - -## Things to note -Some of the fields listed in these webforms are FieldArrays. This means that they are arrays of values that contain groups of like values (think an array of identicaly typed objects). The items within those arrays will have `Parent` values to indicate which parent they belong to. - -## Key structuring of Webforms: -For our webforms, we are in communication with data connect on our we should pass our json data to their team. We have gone with this approach to help streamline our data, but also make it as easy as possible to hunt down questions if items are added or changed. Our “name” keys are made up of for different parts. - -Our agreed upon key structures for the “name” property of form field sections are as follows: - -The Four Parts to the name field are: -- form name '-' instead of dots -- section title separated by “-” shortened if possible -- input id/label as close and short as possible with only '-' as separators -- input type is usually the (rhf) value text/textarea/array/checkgroup/upload/radiogroup/select - -> `[form-name]_[section-title]_[question-id]_[input]` - -Example: **"abp1_pop-id_abp-pop-name_input"** - -**It is imperative that this pattern be followed in all forms.** diff --git a/docs/docs/workflows/auto-create-jira-comment.md b/docs/docs/workflows/auto-create-jira-comment.md deleted file mode 100644 index a41fc38ec9..0000000000 --- a/docs/docs/workflows/auto-create-jira-comment.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: default -title: Jira Issue Commenter -parent: GitHub Workflows -nav_order: 2 ---- - -# Jira Issue Commenter -{: .no_toc } - -Automatically links Pull Requests to Jira Issues mentioned in the PR body. -{: .fs-6 .fw-300 } ---- - -## Summary - -The {{ site.repo.name }} project uses GitHub Pull Requests to review and merge and code change. A GitHub pull request is a feature that allows developers to propose changes to a project's codebase. When a developer wants to suggest changes to a project, they create a pull request which includes the code changes they've made. The pull request then allows other developers to review the proposed changes, discuss any potential issues, and ultimately merge the changes into the main codebase. - -The {{ site.repo.name }} project uses Jira to plan, schedule, and track development work items. - -As a general rule, most pull requests should be related to a Jira Issue. In fact our PR template has a section where you may list related issues. - -The auto-create-jira-comment workflow is meant to scan pull requests for Jira Issue links; any issues that it finds receives a new comment "This issue was referenced on (link to pull request)". If it finds no issue links in the PR, nothing happens. If it finds one, two, or 'n' issues, they all receive that same "This issue was referenced..." comment. While this workflow will not automatically close issues in Jira, it works to create that link between work item and pull request, provided the team can add Jira Issue links to PRs. - -## Configuration, Notes, YSK - -### Set JIRA_USERNAME and JIRA_TOKEN as github secrets - -The workflow file expects two secrets to be set, JIRA_USERNAME and JIRA_TOKEN. IF they're not set, the workflow will not fail, but it will be unable to comment on Jira issues. - -You'll need to create username and token secrets for a jira user. The token is more accurately called a Personal Access Token (PAT) in Jira, and can be created by logging into Jira in a web browser and following [these instructions](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html#UsingPersonalAccessTokens-CreatingPATsintheapplication). - -On MACPro, we use a service user; you probably should, too. If you're on MACPro, you may be able to leverage our existing service user; reach out to {{ site.contact_email }} or Nathan O'Donnell about possible access. - -Load these values for JIRA_USERNAME and JIRA_TOKEN into the repository's actions secrets, and the workflow functionality will be operational. - -### Review/Update the JIRA_BASE_URL in the workflow file - -In the [workflow definition](../../../.github/workflows/autom-create-jira-comment.yml), there is a hardcoded value for JIRA_BASE_URL. This is used to more precisely find Jira issue links. As this value is the same for MACPro projects, it was hardcoded to reduce configuration burden. But if your project uses a different Jira than the one listed, update this value to your Jira base url. \ No newline at end of file diff --git a/docs/docs/workflows/deploy.md b/docs/docs/workflows/deploy.md deleted file mode 100644 index 50e97b732a..0000000000 --- a/docs/docs/workflows/deploy.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -layout: default -title: Deploy -parent: GitHub Workflows -nav_order: 3 ---- - -# Deploy -{: .no_toc } - -Deploys the stage -{: .fs-6 .fw-300 } ---- - -## Summary - -This GitHub workflow deploys an application to AWS and performs various tests and checks on the resources deployed. It consists of several jobs: - -1. **init**: This job validates the name of the branch and ensures that it adheres to the naming convention used by the Serverless Framework. - -2. **deploy**: This job deploys the application to AWS using the Serverless Framework. It checks out the source code, configures AWS credentials, and deploys the application using the `run deploy` command. - -3. **test**: This job runs automated tests on the deployed application. It checks out the source code, configures AWS credentials, and runs the tests using the `run test` command. - -4. **cfn-nag**: This job performs a static analysis of the AWS CloudFormation templates used by the application. It checks out the source code, configures AWS credentials, and uses the `stelligent/cfn_nag` action to analyze the templates. - -5. **resources**: This job retrieves information about the resources deployed to AWS by the application. It checks out the source code, configures AWS credentials, and uses the `aws cloudformation list-stack-resources` command to retrieve information about the resources deployed. - -6. **release**: This job creates a release of the application. It checks out the source code, configures AWS credentials, and creates a GitHub release with the artifacts produced by the `test`, `cfn-nag`, and `resources` jobs. - -## Workflow Details - -- **Name:** Deploy -- **Triggers:** This workflow is triggered on every push to any branch, except for branches that start with "skipci". -- **Concurrency:** This workflow is limited to one concurrent run per branch. -- **Environment Variables:** - - `STAGE_NAME`: The name of the deployment stage. This is set to the name of the branch by default. -- **Permissions:** - - `id-token`: write - - `contents`: write - - `issues`: write - - `pull-requests`: write -- **Jobs:** - 1. **init:** - - **Runs on:** Ubuntu 20.04 - - **Steps:** - - Validate the stage name - 2. **deploy:** - - **Runs on:** Ubuntu 20.04 - - **Needs:** init - - **Environment:** `STAGE_NAME` - - **Steps:** - - Checkout the source code - - Use the `aws-actions/configure-aws-credentials` action to configure AWS credentials - - Deploy the application using the `run deploy` command - 3. **test:** - - **Runs on:** Ubuntu 20.04 - - **Needs:** deploy - - **Environment:** `STAGE_NAME` - - **Steps:** - - Checkout the source code - - Use the `aws-actions/configure-aws-credentials` action to configure AWS credentials - - Run automated tests using the `run test` command - 4. **cfn-nag:** - - **Runs on:** Ubuntu 20.04 - - **Needs:** deploy - - **Environment:** `STAGE_NAME` - - **Steps:** - - Checkout the source code - - Use the `aws-actions/configure-aws-credentials` action to configure AWS credentials - - Use the `stelligent/cfn_nag` action to perform a static analysis of the AWS CloudFormation templates - 5. **resources:** - - **Runs on:** Ubuntu 20.04 - - **Needs:** deploy - - ** diff --git a/docs/docs/workflows/security-hub-jira-sync.md b/docs/docs/workflows/security-hub-jira-sync.md deleted file mode 100644 index 59a597cb70..0000000000 --- a/docs/docs/workflows/security-hub-jira-sync.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: default -title: Security Hub Jira Sync -parent: GitHub Workflows -nav_order: 1 ---- - -# Security Hub Jira Sync -{: .no_toc } - -Reflect our active Security Hub findings in Jira. -{: .fs-6 .fw-300 } ---- - -## Summary - -CMS projects deployed in AWS are required to resolve [Security Hub](https://docs.aws.amazon.com/securityhub/latest/userguide/what-is-securityhub.html) findings according to the following schedule: -- Critical vulnerabilities within 15 days from discovery -- High vulnerabilities within 30 days from discovery -- Moderate (MEDIUM) vulnerabilities within 90 days from discovery -- Low vulnerabilities within 365 days from discovery - -The security-hub-jira-sync workflow exists to get Security Hub findings in a project's AWS account(s) into Jira and in front of the developers equipped to resolve them. The workflow works by running the [macpro-security-hub-sync npm package](https://www.npmjs.com/package/@enterprise-cmcs/macpro-security-hub-sync) on a cron, scheduled for every other hour during business hours. Please see the npm packages documentation for more details. In short: when the package is run, the master branch's AWS account is scanned for SecHub findings, those finding types get issues created in Jira, and any resolved SecHub issues that have a Jira issue have their issue closed. - -## Configuration, Notes, YSK - -### Set ENABLE_SECURITY_HUB_SYNC actions variable -The security hub workflow is unique, in that it should ideally only be run by one project per AWS account. Since security hub findings are scoped to the account, and since we can have many projects deployed to a given account, multiple instances of the security hub workflow running is not ideal. It is rather harmless if two projects in the same account were both running the workflow, but it's best avoided. - -To that end, a new project based off this repo will not run this workflow automatically. There is an environment variable flag that must be set before the job will run. - -To enable the security-hub-jira-sync workflow, set a repository variable named 'ENABLE_SECURITY_HUB_SYNC' to any value. The existence of the variable is what is checked, not its value. To set a GitHub actions secret, follow the same steps as creating an Actions secret, but look for a tab that says 'Variables'. These function just like secrets in their scope, but are unencrypted. - -### Set JIRA_USERNAME and JIRA_TOKEN as github secrets - -Per the [macpro-security-hub-sync](https://www.npmjs.com/package/@enterprise-cmcs/macpro-security-hub-sync) docs, you'll need to create username and token secrets for a jira user. The token is more accurately called a Personal Access Token (PAT) in Jira, and can be created by logging into Jira in a web browser and following [these instructions](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html#UsingPersonalAccessTokens-CreatingPATsintheapplication). - -On MACPro, we use a service user; you probably should, too. If you're on MACPro, you may be able to leverage our existing service user; reach out to {{ site.contact_email }} or Nathan O'Donnell about possible access. - -### Review/Update the JIRA_HOST and JIRA_PROJECT settings - -In the [workflow definition](../../../.github/workflows/security-hub-jira-sync.yml), there are hardcoded values for JIRA_HOST and JIRA_PROJECT. These values are what all projects on MACPro should use, so we've kept it simple and put them directly in the file. If you're not on MACPro or need to change these values, make the appropriate updates to the file. - -### Review/Update the custom fields in src/run.ts - -Some Jira projects require certain fields to be set for issues to be created. This is the case with MACPro's Jira installation. We have two fields that must be set. These fields are set in the [run script](../../../src/run.ts). You'll note two lines that begin with 'customfield_'. Unfortunately, we must set the actual id and value of the custom field as Jira expects it. These ids will vary from instance to instance, even if the field name is the same. Despite our best efforts, the complexity and number of options and allowances when setting custom fields makes it incredibly difficult to have a more user friendly experience; you must find the customfield id's for the values you must set. If you're on MACPro and use our Jira, these are the field values you need, which map to Working Team and Product Supported. If you're not on MACPro and using a different Jira, trial and error is usually fine; the package will surface the Jira API error as it says 'customfield_1234 is not set, Product Supported is not set'. In other words, you can look carefully at the fail output to find the id names. Alternatively, you may also query the Jira API and be more exact about it. - -In any event, you probably want to update the Working Team from Platform Team to your team name. - -### YSK the workflow only runs on the default branch - -On MACPro, we typically use three separate levels of AWS accounts for each project: -- dev account: this holds the default branch (master) enviroment along with all ephemeral branches/environments. -- impl account: this holds the val environment built from the val branch. -- production account: this holds the production environment built from the production branch. - -Obviously, the sechub workflow needs to authenticate and talk to AWS. It gets the arn of the role to assume from a github secret. What you should know is: the scheduled/crond workflow will only kick off on the default/master branch; this is how cron'd workflows behave. This means that your workflow will only automatically manage findings in Jira for the dev aws account. If/when you'd like to scan the impl/production accounts for findings, you may manually run the security hub workflow (workflow_dispatch action) from the GitHub UI. When triggering a build, it will ask off of which branch it should run; you may select val or production as you wish. - -You might be wondering: why can't we work around this? Good question; and the answer is 'we could'. But there are bigger obstacles to automated val/production runs than the cron behavior. For instance, our OIDC role's trust policy pattern will only allow workflows run off of the true val or production branch to assume the aws service role, respectivelly. To add to the issues, if we were to build a mechanism to trigger security hub sync off of val or production so we could assume the role, GitHub would require an administrator to approve the workflow's access to the Environment holding the role arn before it would be allowed to continue. This is all by design, and all very good ideas. By design, having automatic and unattended builds run against val and production is not possible, or at least very difficult. - -In summary: you will only get automatic sec hub scanning for the dev account, which should go a long way to keeping the higher environments safe from vulnerabilities brought about by vulnerable code. Scanning impl and production must be done by manually running the workflow ad hoc, which can be accomplished through the GitHub UI. \ No newline at end of file diff --git a/docs/docs/workflows/workflows.md b/docs/docs/workflows/workflows.md deleted file mode 100644 index 62a8a501d3..0000000000 --- a/docs/docs/workflows/workflows.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -layout: default -title: GitHub Workflows -nav_order: 3 -has_children: true -permalink: docs/workflows ---- - -# GitHub Actions -{: .no_toc } - -Details on our workflows! -{: .fs-6 .fw-300 } - -GitHub Actions is a CI/CD platform that can be used in any GitHub repository. It's organized at the highest level into [workflows](https://docs.github.com/en/actions/using-workflows/about-workflows). Each workflow is defined in its own file located in .github/workflows/. Each workflow file defines one or more jobs to run and when to run them. - -This section will detail each of the project's workflows. Some may require a fair amount of detail, others may be simple and obvious. For sake of portability, each will be documented in its own file in this documentation site. - -If you see any gaps, please let us know! GitHub Actions is really the production floor of our product, and its important to have complete documentation. diff --git a/docs/favicon.ico b/docs/favicon.ico deleted file mode 100644 index 4b60c0d6dc..0000000000 Binary files a/docs/favicon.ico and /dev/null differ diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 446d34eaf1..0000000000 --- a/docs/index.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: default -title: Home -nav_order: 1 -description: "The home page." -permalink: / ---- - -# {{site.title}} -{: .fs-9 } - -{{ site.description }} -{: .fs-6 .fw-300 } - -[Get started now](#getting-started){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } [View it on GitHub]({{ site.repo.url }}){: .btn .fs-5 .mb-4 .mb-md-0 } - ---- - -## Welcome! - -The {{ site.repo.name }} project, a.k.a. MAKO, a.k.a. Micro, is a redesign of MACPRO Onemac. The mission to be a modern submission and review portal for select CMS data remains the same, but the architecture is different in some important ways. - -This documentation site is a WIP and currently shows outdated information, as the project hasn't quite reached the 'norming' phase yet. Thank you for your patience. - ---- - -## About the project - -The {{ site.repo.name }} project is a work of the [Centers for Medicare & Medicaid Services (CMS)](https://www.cms.gov/). - - -#### Thank you to the contributors of {{ site.repo.name }}! - -
    -{% for contributor in site.github.contributors %} -
  • - {{ contributor.login }} -
  • -{% endfor %} -
diff --git a/eslint.config.mjs b/eslint.config.mjs index 6e3b8f5fda..a6a142d197 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -11,46 +11,57 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const gitignorePath = path.resolve(__dirname, ".gitignore"); -export default tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended, includeIgnoreFile(gitignorePath), { - plugins: { - // @ts-expect-error Types mismatch for eslint-plugin-react - react, - // @ts-expect-error https://github.com/facebook/react/pull/28773#issuecomment-2147149016 - "react-hooks": fixupPluginRules(eslintReactHooks), - }, - languageOptions: { - ecmaVersion: 2020, - sourceType: "module", +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + includeIgnoreFile(gitignorePath), + { + plugins: { + // @ts-expect-error Types mismatch for eslint-plugin-react + react, + // @ts-expect-error https://github.com/facebook/react/pull/28773#issuecomment-2147149016 + "react-hooks": fixupPluginRules(eslintReactHooks), + }, + languageOptions: { + ecmaVersion: 2020, + sourceType: "module", - parserOptions: { - ecmaFeatures: { - jsx: true, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, }, }, - }, - settings: { - react: { - version: "detect", + settings: { + react: { + version: "detect", + }, }, - }, - rules: { - "@typescript-eslint/no-empty-interface": "off", - "react/react-in-jsx-scope": "off", - "react/jsx-no-useless-fragment": ["error", { allowExpressions: true }], - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/ban-ts-comment": [ - "off", - { - "ts-ignore": false, - "ts-nocheck": false, - "ts-check": true, - "ts-expect-error": true, - }, - ], + rules: { + "@typescript-eslint/no-empty-interface": "off", + "react/react-in-jsx-scope": "off", + "react/jsx-no-useless-fragment": ["error", { allowExpressions: true }], + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + "@typescript-eslint/no-unused-vars": [ + "error", + { + varsIgnorePattern: "^_", + }, + ], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/ban-ts-comment": [ + "off", + { + "ts-ignore": false, + "ts-nocheck": false, + "ts-check": true, + "ts-expect-error": true, + }, + ], + }, }, -}); +); diff --git a/lib/lambda/getAttachmentUrl.ts b/lib/lambda/getAttachmentUrl.ts index b1b48c0de8..7f94231bd9 100644 --- a/lib/lambda/getAttachmentUrl.ts +++ b/lib/lambda/getAttachmentUrl.ts @@ -1,3 +1,4 @@ +import { handleOpensearchError } from "./utils"; import { response } from "libs/handler-lib"; import { APIGatewayEvent } from "aws-lambda"; import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts"; @@ -83,11 +84,7 @@ export const handler = async (event: APIGatewayEvent) => { body: { url }, }); } catch (error) { - console.error({ error }); - return response({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + return response(handleOpensearchError(error)); } }; diff --git a/lib/lambda/getCpocs.ts b/lib/lambda/getCpocs.ts index f48fa7c9f7..5efc373159 100644 --- a/lib/lambda/getCpocs.ts +++ b/lib/lambda/getCpocs.ts @@ -1,3 +1,4 @@ +import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import * as os from "libs/opensearch-lib"; import { response } from "libs/handler-lib"; @@ -46,11 +47,7 @@ export const getCpocs = async (event: APIGatewayEvent) => { body: result, }); } catch (err) { - console.error({ err }); - return response({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + return response(handleOpensearchError(err)) } }; diff --git a/lib/lambda/getPackageActions.test.ts b/lib/lambda/getPackageActions.test.ts index 4297ae8d89..7edb00baa9 100644 --- a/lib/lambda/getPackageActions.test.ts +++ b/lib/lambda/getPackageActions.test.ts @@ -1,100 +1,77 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; -import { handler } from "./getPackageActions"; -import { response } from "libs/handler-lib"; -import { getAvailableActions } from "shared-utils"; -import { getPackage } from "../libs/api/package/getPackage"; +import { getRequestContext } from "mocks"; import { - getAuthDetails, - isAuthorizedToGetPackageActions, - lookupUserAttributes, -} from "../libs/api/auth/user"; - -vi.mock("libs/handler-lib", () => ({ - response: vi.fn(), -})); - -vi.mock("shared-utils", () => ({ - getAvailableActions: vi.fn(), -})); - -vi.mock("../libs/api/package/getPackage", () => ({ - getPackage: vi.fn(), -})); - -vi.mock("../libs/api/auth/user", () => ({ - getAuthDetails: vi.fn(), - isAuthorizedToGetPackageActions: vi.fn(), - lookupUserAttributes: vi.fn(), -})); + GET_ERROR_ITEM_ID, + HI_TEST_ITEM_ID, + NOT_FOUND_ITEM_ID, + WITHDRAWN_CHANGELOG_ITEM_ID, +} from "mocks/data/items"; +import { describe, expect, it } from "vitest"; +import { handler } from "./getPackageActions"; describe("getPackageActions Handler", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - it("should return 400 if event body is missing", async () => { const event = {} as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 400, - body: { message: "Event body required" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(400); }); it("should return 401 if not authorized to view resources from the state", async () => { - const packageData = { found: true, _source: { state: "test-state" } }; - (getPackage as vi.Mock).mockResolvedValueOnce(packageData); - (isAuthorizedToGetPackageActions as vi.Mock).mockResolvedValueOnce(false); + const event = { + body: JSON.stringify({ id: HI_TEST_ITEM_ID }), + requestContext: getRequestContext(), + } as APIGatewayEvent; + const res = await handler(event); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(401); + expect(res.body).toEqual( + JSON.stringify({ message: "Not authorized to view resources from this state" }), + ); + }); + + it("should return 404 if the package is not found", async () => { const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: NOT_FOUND_ITEM_ID }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 401, - body: { message: "Not authorized to view resources from this state" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(404); + expect(res.body).toEqual(JSON.stringify({ message: "No record found for the given id" })); }); it("should return 200 with available actions if authorized and package is found", async () => { - const packageData = { found: true, _source: { state: "test-state" } }; - const userAttributes = { userId: "test-user", poolId: "test-pool" }; - const actions = ["action1", "action2"]; - (getPackage as vi.Mock).mockResolvedValueOnce(packageData); - (isAuthorizedToGetPackageActions as vi.Mock).mockResolvedValueOnce(true); - (getAuthDetails as vi.Mock).mockReturnValueOnce(userAttributes); - (lookupUserAttributes as vi.Mock).mockResolvedValueOnce(userAttributes); - (getAvailableActions as vi.Mock).mockReturnValueOnce(actions); - const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: WITHDRAWN_CHANGELOG_ITEM_ID }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 200, - body: { actions }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual(JSON.stringify({ actions: [] })); }); it("should handle errors during processing", async () => { - (getPackage as vi.Mock).mockRejectedValueOnce(new Error("Test error")); - const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: GET_ERROR_ITEM_ID }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual( + JSON.stringify({ error: "Internal server error", message: "Response Error" }), + ); }); }); diff --git a/lib/lambda/getPackageActions.ts b/lib/lambda/getPackageActions.ts index 23063d1756..312a9e9b0c 100644 --- a/lib/lambda/getPackageActions.ts +++ b/lib/lambda/getPackageActions.ts @@ -1,12 +1,13 @@ import { APIGatewayEvent } from "aws-lambda"; +import { response } from "libs/handler-lib"; import { getAvailableActions } from "shared-utils"; -import { getPackage } from "../libs/api/package/getPackage"; +import { handleOpensearchError } from "./utils"; import { getAuthDetails, isAuthorizedToGetPackageActions, lookupUserAttributes, } from "../libs/api/auth/user"; -import { response } from "libs/handler-lib"; +import { getPackage } from "../libs/api/package/getPackage"; export const getPackageActions = async (event: APIGatewayEvent) => { if (!event.body) { @@ -15,12 +16,13 @@ export const getPackageActions = async (event: APIGatewayEvent) => { body: { message: "Event body required" }, }); } - const body = JSON.parse(event.body); try { + const body = JSON.parse(event.body); + const result = await getPackage(body.id); - if (result === undefined) { + if (result === undefined || !result.found) { return response({ statusCode: 404, body: { message: "No record found for the given id" }, @@ -35,11 +37,6 @@ export const getPackageActions = async (event: APIGatewayEvent) => { body: { message: "Not authorized to view resources from this state" }, }); - if (!result.found) - return response({ - statusCode: 404, - body: { message: "No record found for the given id" }, - }); const authDetails = getAuthDetails(event); const userAttr = await lookupUserAttributes(authDetails.userId, authDetails.poolId); @@ -50,11 +47,7 @@ export const getPackageActions = async (event: APIGatewayEvent) => { }, }); } catch (err) { - console.error({ err }); - return response({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + return response(handleOpensearchError(err)); } }; export const handler = getPackageActions; diff --git a/lib/lambda/getSubTypes.ts b/lib/lambda/getSubTypes.ts index 57c9cf9318..8ea4fff1ac 100644 --- a/lib/lambda/getSubTypes.ts +++ b/lib/lambda/getSubTypes.ts @@ -1,3 +1,4 @@ +import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import * as os from "libs/opensearch-lib"; import { response } from "libs/handler-lib"; @@ -77,11 +78,7 @@ export const getSubTypes = async (event: APIGatewayEvent) => { body: result, }); } catch (err) { - console.error({ err }); - return response({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + return response(handleOpensearchError(err)) } }; diff --git a/lib/lambda/getTypes.ts b/lib/lambda/getTypes.ts index 009e9ee615..813e62ed6c 100644 --- a/lib/lambda/getTypes.ts +++ b/lib/lambda/getTypes.ts @@ -1,3 +1,4 @@ +import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import * as os from "libs/opensearch-lib"; import { response } from "libs/handler-lib"; @@ -69,11 +70,7 @@ export const getTypes = async (event: APIGatewayEvent) => { body: result, }); } catch (err) { - console.error({ err }); - return response({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + return response(handleOpensearchError(err)); } }; diff --git a/lib/lambda/item.test.ts b/lib/lambda/item.test.ts index 521cb55d1d..a874595faf 100644 --- a/lib/lambda/item.test.ts +++ b/lib/lambda/item.test.ts @@ -1,121 +1,80 @@ -import { describe, it, expect, vi, beforeEach, Mock } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; -import { handler } from "./item"; -import { response } from "../libs/handler-lib"; -import { getStateFilter } from "../libs/api/auth/user"; import { - getAppkChildren, - getPackage, - getPackageChangelog, -} from "../libs/api/package"; - -vi.mock("libs/handler-lib", () => ({ - response: vi.fn(), -})); + GET_ERROR_ITEM_ID, + HI_TEST_ITEM_ID, + NOT_FOUND_ITEM_ID, + WITHDRAWN_CHANGELOG_ITEM_ID, + getRequestContext, +} from "mocks"; +import items from "mocks/data/items"; +import { describe, expect, it } from "vitest"; -vi.mock("../libs/api/auth/user", () => ({ - getStateFilter: vi.fn(), -})); - -vi.mock("../libs/api/package", () => ({ - getAppkChildren: vi.fn(), - getPackage: vi.fn(), - getPackageChangelog: vi.fn(), -})); +import { handler } from "./item"; describe("getItemData Handler", () => { - beforeEach(() => { - vi.clearAllMocks(); - process.env.osDomain = "test-domain"; // Set the environment variable before each test - }); - it("should return 400 if event body is missing", async () => { const event = {} as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 400, - body: { message: "Event body required" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(400); + expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })); }); it("should return 401 if not authorized to view this resource", async () => { - const packageData = { found: true, _source: { state: "test-state" } }; - (getPackage as Mock).mockResolvedValueOnce(packageData); - (getStateFilter as Mock).mockResolvedValueOnce({ - terms: { state: ["other-state"] }, - }); + const event = { + body: JSON.stringify({ id: HI_TEST_ITEM_ID }), + requestContext: getRequestContext(), + } as APIGatewayEvent; + + const res = await handler(event); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(401); + expect(res.body).toEqual(JSON.stringify({ message: "Not authorized to view this resource" })); + }); + it("should return 404 if the item is not found", async () => { const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: NOT_FOUND_ITEM_ID }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 401, - body: { message: "Not authorized to view this resource" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(404); + expect(res.body).toEqual(JSON.stringify({ message: "No record found for the given id" })); }); it("should return 200 with package data, children, and changelog if authorized", async () => { - const packageData = { - found: true, - _source: { - state: "test-state", - appkParent: true, - legacySubmissionTimestamp: null, - }, - }; - const childrenData = { - hits: { hits: [{ _source: { child: "child-data" } }] }, - }; - const changelogData = { - hits: { hits: [{ _source: { change: "change-data" } }] }, - }; - - (getPackage as Mock).mockResolvedValueOnce(packageData); - (getStateFilter as Mock).mockResolvedValueOnce({ - terms: { state: ["test-state"] }, - }); - (getAppkChildren as Mock).mockResolvedValueOnce(childrenData); - (getPackageChangelog as Mock).mockResolvedValueOnce(changelogData); + const packageData = items[WITHDRAWN_CHANGELOG_ITEM_ID]; const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: WITHDRAWN_CHANGELOG_ITEM_ID }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); - - expect(response).toHaveBeenCalledWith({ - statusCode: 200, - body: { - ...packageData, - _source: { - ...packageData._source, - appkChildren: childrenData.hits.hits, - changelog: changelogData.hits.hits, - }, - }, - }); + const res = await handler(event); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual(JSON.stringify(packageData)); }); it("should return 500 if an error occurs during processing", async () => { - (getPackage as Mock).mockRejectedValueOnce(new Error("Test error")); - const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: GET_ERROR_ITEM_ID }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { - error: new Error("Test error"), - message: "Test error", - }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual( + JSON.stringify({ error: "Internal server error", message: "Response Error" }), + ); }); }); diff --git a/lib/lambda/item.ts b/lib/lambda/item.ts index 80f4f9257a..ee5c452ef7 100644 --- a/lib/lambda/item.ts +++ b/lib/lambda/item.ts @@ -1,12 +1,9 @@ -import { response } from "../libs/handler-lib"; +import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; -import { getStateFilter } from "../libs/api/auth/user"; -import { - getAppkChildren, - getPackage, - getPackageChangelog, -} from "../libs/api/package"; import { validateEnvVariable } from "shared-utils"; +import { getStateFilter } from "../libs/api/auth/user"; +import { getAppkChildren, getPackage, getPackageChangelog } from "../libs/api/package"; +import { response } from "../libs/handler-lib"; export const getItemData = async (event: APIGatewayEvent) => { validateEnvVariable("osDomain"); @@ -16,50 +13,51 @@ export const getItemData = async (event: APIGatewayEvent) => { body: { message: "Event body required" }, }); } + try { const body = JSON.parse(event.body); - const stateFilter = await getStateFilter(event); + const packageResult = await getPackage(body.id); + if (packageResult === undefined || !packageResult.found) { + return response({ + statusCode: 404, + body: { message: "No record found for the given id" }, + }); + } + + const stateFilter = await getStateFilter(event); + if ( + stateFilter && + (!packageResult?._source.state || + !stateFilter.terms.state.includes(packageResult._source.state.toLocaleLowerCase())) + ) { + return response({ + statusCode: 401, + body: { message: "Not authorized to view this resource" }, + }); + } let appkChildren: any[] = []; - if (packageResult._source.appkParent) { + if (packageResult?._source?.appkParent) { const children = await getAppkChildren(body.id); appkChildren = children.hits.hits; } const filter = []; // This is to handle hard deletes in legacy - if (packageResult._source.legacySubmissionTimestamp !== null) { + if ( + packageResult?._source?.legacySubmissionTimestamp !== null && + packageResult?._source?.legacySubmissionTimestamp !== undefined + ) { filter.push({ range: { timestamp: { - gte: new Date( - packageResult._source.legacySubmissionTimestamp, - ).getTime(), + gte: new Date(packageResult._source.legacySubmissionTimestamp).getTime(), }, }, }); } const changelog = await getPackageChangelog(body.id, filter); - if ( - stateFilter && - (!packageResult._source.state || - !stateFilter.terms.state.includes( - packageResult._source.state.toLocaleLowerCase(), - )) - ) { - return response({ - statusCode: 401, - body: { message: "Not authorized to view this resource" }, - }); - } - - if (!packageResult.found) { - return response({ - statusCode: 404, - body: { message: "No record found for the given id" }, - }); - } return response({ statusCode: 200, @@ -73,10 +71,7 @@ export const getItemData = async (event: APIGatewayEvent) => { }, }); } catch (error) { - return response({ - statusCode: 500, - body: { error, message: error.message }, - }); + return response(handleOpensearchError(error)); } }; diff --git a/lib/lambda/itemExists.test.ts b/lib/lambda/itemExists.test.ts index 111d810b66..fba774d836 100644 --- a/lib/lambda/itemExists.test.ts +++ b/lib/lambda/itemExists.test.ts @@ -1,77 +1,71 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; +import { GET_ERROR_ITEM_ID, NOT_FOUND_ITEM_ID, TEST_ITEM_ID, NOT_EXISTING_ITEM_ID } from "mocks"; +import { describe, expect, it } from "vitest"; import { handler } from "./itemExists"; -import { response } from "libs/handler-lib"; -import * as os from "../libs/opensearch-lib"; - -vi.mock("libs/handler-lib", () => ({ - response: vi.fn(), -})); - -vi.mock("../libs/opensearch-lib", () => ({ - getItem: vi.fn(), -})); describe("Handler for checking if record exists", () => { - beforeEach(() => { - vi.clearAllMocks(); - process.env.osDomain = "test-domain"; - process.env.indexNamespace = "test-namespace-"; - }); - it("should return 400 if event body is missing", async () => { const event = {} as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 400, - body: { message: "Event body required" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(400); }); it("should return 200 and exists: true if record is found", async () => { - (os.getItem as vi.Mock).mockResolvedValueOnce({ _source: {} }); - const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: TEST_ITEM_ID }), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 200, - body: { message: "Record found for the given id", exists: true }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual( + JSON.stringify({ message: "Record found for the given id", exists: true }), + ); }); it("should return 200 and exists: false if no record is found", async () => { - (os.getItem as vi.Mock).mockResolvedValueOnce({}); - const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: NOT_FOUND_ITEM_ID }), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 200, - body: { message: "No record found for the given id", exists: false }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual( + JSON.stringify({ message: "No record found for the given id", exists: false }), + ); }); - it.skip("should return 500 if an error occurs during processing", async () => { - (os.getItem as vi.Mock).mockRejectedValueOnce(new Error("Test error")); + it("should return 200 and exists: false if a 404 error occurs during processing", async () => { + const event = { + body: JSON.stringify({ id: NOT_EXISTING_ITEM_ID }), + } as APIGatewayEvent; + + const res = await handler(event); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual( + JSON.stringify({ message: "No record found for the given id", exists: false }), + ); + }); + it("should return 500 error occurs during processing", async () => { const event = { - body: JSON.stringify({ id: "test-id" }), + body: JSON.stringify({ id: GET_ERROR_ITEM_ID }), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual( + JSON.stringify({ error: "Internal server error", message: "Response Error" }), + ); }); }); diff --git a/lib/lambda/itemExists.ts b/lib/lambda/itemExists.ts index 19e0d4a490..29b2d72df6 100644 --- a/lib/lambda/itemExists.ts +++ b/lib/lambda/itemExists.ts @@ -1,6 +1,7 @@ -import { response } from "libs/handler-lib"; +import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import { itemExists } from "libs/api/package"; +import { response } from "libs/handler-lib"; export const handler = async (event: APIGatewayEvent) => { if (!event.body) { @@ -9,6 +10,7 @@ export const handler = async (event: APIGatewayEvent) => { body: { message: "Event body required" }, }); } + try { const body = JSON.parse(event.body); const exists = await itemExists({ @@ -17,17 +19,11 @@ export const handler = async (event: APIGatewayEvent) => { return response({ statusCode: 200, body: { - message: exists - ? "Record found for the given id" - : "No record found for the given id", + message: exists ? "Record found for the given id" : "No record found for the given id", exists, }, }); } catch (error) { - console.error({ error }); - return response({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + return response(handleOpensearchError(error)); } }; diff --git a/lib/lambda/search.test.ts b/lib/lambda/search.test.ts index fd4387b67c..d9cad9d9bc 100644 --- a/lib/lambda/search.test.ts +++ b/lib/lambda/search.test.ts @@ -1,110 +1,50 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { APIGatewayEvent } from "aws-lambda"; +import { APIGatewayEvent, APIGatewayProxyEventPathParameters } from "aws-lambda"; +import { getRequestContext, makoStateSubmitter } from "mocks"; +import { describe, expect, it } from "vitest"; import { handler } from "./search"; -import { response } from "libs/handler-lib"; - -import * as os from "../libs/opensearch-lib"; -import { getAppkChildren } from "../libs/api/package"; - -vi.mock("libs/handler-lib", () => ({ - response: vi.fn(), -})); - -vi.mock("../libs/api/auth/user", () => ({ - getStateFilter: vi.fn(), -})); - -vi.mock("../libs/opensearch-lib", () => ({ - search: vi.fn(), -})); - -vi.mock("../libs/api/package", () => ({ - getAppkChildren: vi.fn(), -})); describe("getSearchData Handler", () => { - beforeEach(() => { - vi.clearAllMocks(); - process.env.osDomain = "test-domain"; // Set the environment variable before each test - process.env.indexNamespace = "test-namespace-"; // Set the environment variable before each test - }); - it("should call validateEnvVariable and return 400 if index path parameter is missing", async () => { const event = { pathParameters: {} } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 400, - body: { message: "Index path parameter required" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(400); + expect(res.body).toEqual(JSON.stringify({ message: "Index path parameter required" })); }); it("should return 200 with search results", async () => { - const mockResults = { - hits: { - hits: [ - { - _id: "1", - _source: { appkParent: true }, - }, - ], - }, - }; - - (os.search as vi.Mock).mockResolvedValueOnce(mockResults); - (getAppkChildren as vi.Mock).mockResolvedValueOnce({ - hits: { - hits: [{ _id: "2", _source: { child: "child-data" } }], - }, - }); - const event = { - pathParameters: { index: "main" }, body: JSON.stringify({ query: { match_all: {} } }), + pathParameters: { index: "main" } as APIGatewayProxyEventPathParameters, + requestContext: getRequestContext(makoStateSubmitter), } as APIGatewayEvent; - await handler(event); - - expect(os.search).toHaveBeenCalledWith( - "test-domain", - "test-namespace-main", - expect.any(Object), - ); + const res = await handler(event); - expect(getAppkChildren).toHaveBeenCalledWith("1"); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); - expect(response).toHaveBeenCalledWith({ - statusCode: 200, - body: { - hits: { - hits: [ - { - _id: "1", - _source: { - appkParent: true, - appkChildren: [{ _id: "2", _source: { child: "child-data" } }], - }, - }, - ], - }, - }, - }); + const body = JSON.parse(res.body); + expect(body).toBeTruthy(); + expect(body?.hits?.hits).toBeTruthy(); + expect(body?.hits?.hits?.length).toEqual(11); }); it("should handle errors during processing", async () => { - (os.search as vi.Mock).mockRejectedValueOnce(new Error("Test error")); - const event = { - pathParameters: { index: "main" }, - body: JSON.stringify({ query: { match_all: {} } }), + body: JSON.stringify({ query: { match_all: "throw-error" } }), + pathParameters: { index: "main" } as APIGatewayProxyEventPathParameters, + requestContext: getRequestContext(makoStateSubmitter), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual( + JSON.stringify({ error: "Internal server error", message: "Response Error" }), + ); }); }); diff --git a/lib/lambda/search.ts b/lib/lambda/search.ts index 0b37511fc0..a4af6bfa05 100644 --- a/lib/lambda/search.ts +++ b/lib/lambda/search.ts @@ -1,10 +1,11 @@ -import { response } from "libs/handler-lib"; +import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; -import { getStateFilter } from "../libs/api/auth/user"; -import * as os from "../libs/opensearch-lib"; +import { response } from "libs/handler-lib"; import { Index } from "shared-types/opensearch"; -import { getAppkChildren } from "../libs/api/package"; import { validateEnvVariable } from "shared-utils"; +import { getStateFilter } from "../libs/api/auth/user"; +import { getAppkChildren } from "../libs/api/package"; +import * as os from "../libs/opensearch-lib"; // Handler function to search index export const getSearchData = async (event: APIGatewayEvent) => { @@ -45,10 +46,10 @@ export const getSearchData = async (event: APIGatewayEvent) => { query, ); - for (let i = 0; i < results.hits.hits.length; i++) { - if (results.hits.hits[i]._source.appkParent) { + for (let i = 0; i < results?.hits?.hits?.length; i++) { + if (results.hits.hits[i]._source?.appkParent) { const children = await getAppkChildren(results.hits.hits[i]._id); - if (children.hits?.hits.length > 0) { + if (children?.hits?.hits?.length > 0) { results.hits.hits[i]._source.appkChildren = children.hits.hits; } } @@ -59,11 +60,7 @@ export const getSearchData = async (event: APIGatewayEvent) => { body: results, }); } catch (error) { - console.error({ error }); - return response({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + return response(handleOpensearchError(error)); } }; diff --git a/lib/lambda/sinkChangelog.ts b/lib/lambda/sinkChangelog.ts index 90901c549b..9c2fcae00b 100644 --- a/lib/lambda/sinkChangelog.ts +++ b/lib/lambda/sinkChangelog.ts @@ -1,18 +1,7 @@ import { Handler } from "aws-lambda"; import { decodeBase64WithUtf8 } from "shared-utils"; import { KafkaEvent, KafkaRecord, opensearch } from "shared-types"; -import { - ErrorType, - bulkUpdateDataWrapper, - getTopic, - logError, -} from "../libs/sink-lib"; -import { Index } from "shared-types/opensearch"; -const osDomain = process.env.osDomain; -if (!osDomain) { - throw new Error("Missing required environment variable(s)"); -} -const index: Index = `${process.env.indexNamespace}changelog`; +import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; // One notable difference between this handler and sinkMain's... // The order in which records are processed for the changelog doesn't matter. @@ -34,8 +23,6 @@ export const handler: Handler = async (event) => { // await onemac(event.records[topicPartition], topicPartition); await processAndIndex({ kafkaRecords: event.records[topicPartition], - index, - osDomain, transforms: opensearch.changelog.transforms, topicPartition: topicPartition, }); @@ -50,19 +37,14 @@ export const handler: Handler = async (event) => { const processAndIndex = async ({ kafkaRecords, - index, - osDomain, transforms, topicPartition, }: { kafkaRecords: KafkaRecord[]; - index: Index; - osDomain: string; transforms: any; topicPartition: string; }) => { - const docs: Array<(typeof transforms)[keyof typeof transforms]["Schema"]> = - []; + const docs: Array<(typeof transforms)[keyof typeof transforms]["Schema"]> = []; for (const kafkaRecord of kafkaRecords) { console.log(JSON.stringify(kafkaRecord, null, 2)); const { value, offset } = kafkaRecord; @@ -86,8 +68,7 @@ const processAndIndex = async ({ console.log(record.event); if (record.event in transforms) { - const transformForEvent = - transforms[record.event as keyof typeof transforms]; + const transformForEvent = transforms[record.event as keyof typeof transforms]; const result = transformForEvent.transform(offset).safeParse(record); @@ -114,194 +95,5 @@ const processAndIndex = async ({ } } - // Send all transformed records for indexing - await bulkUpdateDataWrapper(osDomain, index, docs); + await bulkUpdateDataWrapper(docs, "changelog"); }; - -// const onemac = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { -// let docs: any[] = []; -// for (const kafkaRecord of kafkaRecords) { -// const { key, value, offset, timestamp } = kafkaRecord; -// try { -// // Skip delete events -// if (!value) continue; - -// // Set id -// const id: string = decodeBase64WithUtf8(key); - -// // Parse event data -// const record = JSON.parse(decodeBase64WithUtf8(value)); - -// // Process legacy events -// if (record?.origin !== "mako") { -// // Skip if it's not a submission event with a good GSIpk -// if ( -// record?.sk === "Package" || -// !record.GSI1pk?.startsWith("OneMAC#submit") -// ) { -// continue; -// } -// const result = opensearch.changelog.legacyEvent -// .transform(id) -// .safeParse(record); - -// if (result.success && result.data === undefined) continue; - -// // Log Error and skip if transform had an error -// if (!result?.success) { -// logError({ -// type: ErrorType.VALIDATION, -// error: result?.error, -// metadata: { topicPartition, kafkaRecord, record }, -// }); -// continue; -// } - -// // If we made it this far, we push the document to the docs array so it gets indexed -// docs.push(result.data); -// } - -// // Process micro events -// if (record?.origin === "mako") { -// // Resolve actionType -// const actionType = record.actionType || "new-submission"; - -// // Push to docs so it can be indexed, with some differences if app k -// if (actionType === Action.UPDATE_ID) { -// console.log("UPDATE_ID detected..."); -// await bulkUpdateDataWrapper(osDomain, index, docs); -// docs = []; -// const items = await os.search(osDomain, index, { -// from: 0, -// size: 200, -// query: { -// bool: { -// must: [{ term: { "packageId.keyword": id } }], -// }, -// }, -// }); -// if (items === undefined || items.hits.hits === undefined) { -// continue; -// } -// const modifiedHits: opensearch.changelog.Document[] = -// items.hits.hits.map( -// (hit: { _source: opensearch.changelog.Document }) => { -// return { -// ...hit._source, -// id: `${record.newId}-${hit._source.id.split("-").pop()}`, -// packageId: record.newId, -// }; -// }, -// ); -// docs.push(...modifiedHits); -// docs.push({ -// ...record, -// id: `${record.newId}-${offset}`, -// packageId: record.newId, -// oldPackageId: id, -// newPackageId: record.newId, -// timestamp, -// actionType, -// }); -// continue; -// } -// if (actionType === Action.REMOVE_APPK_CHILD) { -// docs.push( -// { -// ...record, -// id: `${record.appkParentId}-${offset}`, -// packageId: record.appkParentId, -// appkChildId: record.id, -// timestamp, -// actionType, -// }, -// { -// ...record, -// id: `${record.id}-${offset}`, -// appkParentId: record.appkParentId, -// packageId: record.id, -// timestamp, -// actionType, -// }, -// ); -// continue; -// } -// docs.push({ -// ...record, -// id: `${id}-${offset}`, -// packageId: id, -// timestamp, -// actionType, -// }); -// } -// } catch (error) { -// logError({ -// type: ErrorType.BADPARSE, -// error, -// metadata: { topicPartition, kafkaRecord }, -// }); -// } -// } -// await bulkUpdateDataWrapper(osDomain, index, docs); -// }; - -// const legacyAdminChanges = async ( -// kafkaRecords: KafkaRecord[], -// topicPartition: string, -// ) => { -// const docs: any[] = []; -// for (const kafkaRecord of kafkaRecords) { -// const { key, value } = kafkaRecord; -// try { -// // Skip delete events -// if (!value) continue; - -// // Set id -// const id: string = decodeBase64WithUtf8(key); - -// // Parse event data -// const record = JSON.parse(decodeBase64WithUtf8(value)); - -// // Process legacy events -// if (record?.origin !== "mako") { -// // Skip if it's not a package view from onemac with adminChanges -// if ( -// !( -// record?.sk === "Package" && -// record.submitterName && -// record.adminChanges -// ) -// ) { -// continue; -// } -// for (const adminChange of record.adminChanges) { -// const result = opensearch.changelog.legacyAdminChange -// .transform(id) -// .safeParse(adminChange); - -// if (result.success && result.data === undefined) continue; - -// // Log Error and skip if transform had an error -// if (!result?.success) { -// logError({ -// type: ErrorType.VALIDATION, -// error: result?.error, -// metadata: { topicPartition, kafkaRecord, record }, -// }); -// continue; -// } - -// // If we made it this far, we push the document to the docs array so it gets indexed -// docs.push(result.data); -// } -// } -// } catch (error) { -// logError({ -// type: ErrorType.BADPARSE, -// error, -// metadata: { topicPartition, kafkaRecord }, -// }); -// } -// } -// await bulkUpdateDataWrapper(osDomain, index, docs); -// }; diff --git a/lib/lambda/sinkCpocs.ts b/lib/lambda/sinkCpocs.ts index a718aa26e6..cd6357dbfb 100644 --- a/lib/lambda/sinkCpocs.ts +++ b/lib/lambda/sinkCpocs.ts @@ -1,14 +1,8 @@ import { Handler } from "aws-lambda"; import { KafkaRecord, opensearch } from "shared-types"; import { KafkaEvent } from "shared-types"; -import { - ErrorType, - bulkUpdateDataWrapper, - getTopic, - logError, -} from "../libs/sink-lib"; +import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; import { decodeBase64WithUtf8 } from "shared-utils"; -import { Index } from "shared-types/opensearch"; export const handler: Handler = async (event) => { const loggableEvent = { ...event, records: "too large to display" }; @@ -30,10 +24,7 @@ export const handler: Handler = async (event) => { } }; -const officers = async ( - kafkaRecords: KafkaRecord[], - topicPartition: string, -) => { +const officers = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { const docs: any[] = []; for (const kafkaRecord of kafkaRecords) { const { key, value } = kafkaRecord; @@ -52,9 +43,7 @@ const officers = async ( // Handle tombstone events and continue if (!record) { - console.log( - `Tombstone detected for ${id}. Pushing delete record to os...`, - ); + console.log(`Tombstone detected for ${id}. Pushing delete record to os...`); docs.push({ id, delete: true, @@ -81,11 +70,5 @@ const officers = async ( } } - const osDomain = process.env.osDomain; - if (!osDomain) { - throw new Error("Missing required environment variable(s)"); - } - - const index: Index = `${process.env.indexNamespace}cpocs`; - await bulkUpdateDataWrapper(osDomain, index, docs); + await bulkUpdateDataWrapper(docs, "cpocs"); }; diff --git a/lib/lambda/sinkInsights.ts b/lib/lambda/sinkInsights.ts index 46b37c5e2b..9b3e0c27eb 100644 --- a/lib/lambda/sinkInsights.ts +++ b/lib/lambda/sinkInsights.ts @@ -1,18 +1,7 @@ import { Handler } from "aws-lambda"; import { decodeBase64WithUtf8 } from "shared-utils"; import { KafkaEvent, KafkaRecord } from "shared-types"; -import { - ErrorType, - bulkUpdateDataWrapper, - getTopic, - logError, -} from "../libs/sink-lib"; -import { Index } from "shared-types/opensearch"; -const osDomain = process.env.osDomain; -if (!osDomain) { - throw new Error("Missing required environment variable(s)"); -} -const index: Index = `${process.env.indexNamespace}insights`; +import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; export const handler: Handler = async (event) => { const loggableEvent = { ...event, records: "too large to display" }; @@ -52,5 +41,5 @@ const ksql = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { }); } } - await bulkUpdateDataWrapper(osDomain, index, docs); + await bulkUpdateDataWrapper(docs, "insights"); }; diff --git a/lib/lambda/sinkLegacyInsights.ts b/lib/lambda/sinkLegacyInsights.ts index d1a7d76bbd..023238f32a 100644 --- a/lib/lambda/sinkLegacyInsights.ts +++ b/lib/lambda/sinkLegacyInsights.ts @@ -1,18 +1,7 @@ import { Handler } from "aws-lambda"; import { decodeBase64WithUtf8 } from "shared-utils"; import { KafkaEvent, KafkaRecord } from "shared-types"; -import { - ErrorType, - bulkUpdateDataWrapper, - getTopic, - logError, -} from "../libs/sink-lib"; -import { Index } from "shared-types/opensearch"; -const osDomain = process.env.osDomain; -if (!osDomain) { - throw new Error("Missing required environment variable(s)"); -} -const index: Index = `${process.env.indexNamespace}legacyinsights`; +import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; export const handler: Handler = async (event) => { const loggableEvent = { ...event, records: "too large to display" }; @@ -69,5 +58,5 @@ const onemac = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { }); } } - await bulkUpdateDataWrapper(osDomain, index, docs); + await bulkUpdateDataWrapper(docs, "legacyinsights"); }; diff --git a/lib/lambda/sinkMain.test.ts b/lib/lambda/sinkMain.test.ts new file mode 100644 index 0000000000..1efa64a139 --- /dev/null +++ b/lib/lambda/sinkMain.test.ts @@ -0,0 +1,74 @@ +import { describe, it, expect, vi, afterEach } from "vitest"; +import { handler } from "./sinkMain"; +import * as sinkMainProcessors from "./sinkMainProcessors"; +import { KafkaEvent } from "lib/packages/shared-types"; + +const createKafkaEvent = (records: KafkaEvent["records"]) => ({ + eventSource: "SelfManagedKafka", + bootstrapServers: "kafka", + records, +}); + +describe("sinkMain handler", () => { + vi.stubEnv("osDomain", "os-domain"); + vi.stubEnv("indexNamespace", "index-namespace"); + + afterEach(() => { + vi.restoreAllMocks(); + vi.resetModules(); + }); + + it("handles aws.onemac.migration.cdc topic successfully", async () => { + const spiedOnProcessAndIndex = vi + .spyOn(sinkMainProcessors, "insertOneMacRecordsFromKafkaIntoMako") + .mockImplementation(vi.fn()); + + await handler( + createKafkaEvent({ "aws.onemac.migration.cdc-0": [] }), + expect.anything(), + vi.fn(), + ); + + expect(spiedOnProcessAndIndex).toBeCalledWith([], "aws.onemac.migration.cdc-0"); + }); + + it("handles aws.seatool.ksql.onemac.three.agg.State_Plan topic successfully", async () => { + const spiedOnProcessAndIndex = vi + .spyOn(sinkMainProcessors, "insertNewSeatoolRecordsFromKafkaIntoMako") + .mockImplementation(vi.fn()); + + await handler( + createKafkaEvent({ "aws.seatool.ksql.onemac.three.agg.State_Plan-0": [] }), + expect.anything(), + vi.fn(), + ); + + expect(spiedOnProcessAndIndex).toBeCalledWith( + [], + "aws.seatool.ksql.onemac.three.agg.State_Plan-0", + ); + }); + + it("handles aws.seatool.debezium.changed_date.SEA.dbo.State_Plan topic successfully", async () => { + const spiedOnProcessAndIndex = vi + .spyOn(sinkMainProcessors, "syncSeatoolRecordDatesFromKafkaWithMako") + .mockImplementation(vi.fn()); + + await handler( + createKafkaEvent({ "aws.seatool.debezium.changed_date.SEA.dbo.State_Plan-0": [] }), + expect.anything(), + vi.fn(), + ); + + expect(spiedOnProcessAndIndex).toBeCalledWith( + [], + "aws.seatool.debezium.changed_date.SEA.dbo.State_Plan-0", + ); + }); + + it("throws error with invalid topic partition", async () => { + await expect( + handler(createKafkaEvent({ "invalid-topic-partition": [] }), expect.anything(), vi.fn()), + ).rejects.toThrowError("topic (invalid-topic-partition) is invalid"); + }); +}); diff --git a/lib/lambda/sinkMain.ts b/lib/lambda/sinkMain.ts index 186735d813..1bd794c04b 100644 --- a/lib/lambda/sinkMain.ts +++ b/lib/lambda/sinkMain.ts @@ -1,236 +1,43 @@ import { Handler } from "aws-lambda"; -import { KafkaRecord, opensearch } from "shared-types"; import { KafkaEvent } from "shared-types"; -import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; -import { Index } from "shared-types/opensearch"; -import { decodeBase64WithUtf8 } from "shared-utils"; -import * as os from "./../libs/opensearch-lib"; - -const osDomain = process.env.osDomain; -const indexNamespace = process.env.indexNamespace; -if (!indexNamespace || !osDomain) { - throw new Error("Missing required environment variable(s)"); -} -const index: Index = `${process.env.indexNamespace}main`; +import { ErrorType, getTopic, logError } from "libs"; +import { + insertOneMacRecordsFromKafkaIntoMako, + insertNewSeatoolRecordsFromKafkaIntoMako, + syncSeatoolRecordDatesFromKafkaWithMako, +} from "./sinkMainProcessors"; export const handler: Handler = async (event) => { - console.log("event"); - console.log(JSON.stringify(event, null, 2)); - const loggableEvent = { ...event, records: "too large to display" }; - console.log("loggableEvent"); - console.log(loggableEvent); - try { - for (const topicPartition of Object.keys(event.records)) { - const topic = getTopic(topicPartition); - console.log("topics"); - console.log(topic); - switch (topic) { - case undefined: - logError({ type: ErrorType.BADTOPIC }); - throw new Error(); - case "aws.onemac.migration.cdc": - await processAndIndex({ - kafkaRecords: event.records[topicPartition], - index, - osDomain, - transforms: opensearch.main.transforms, - topicPartition: topicPartition, - }); - break; - case "aws.seatool.ksql.onemac.three.agg.State_Plan": - await ksql(event.records[topicPartition], topicPartition); - break; - case "aws.seatool.debezium.changed_date.SEA.dbo.State_Plan": - await changed_date(event.records[topicPartition], topicPartition); - break; - } - } - } catch (error) { - logError({ type: ErrorType.UNKNOWN, metadata: { event: loggableEvent } }); - throw error; - } -}; - -const processAndIndex = async ({ - kafkaRecords, - index, - osDomain, - transforms, - topicPartition, -}: { - kafkaRecords: KafkaRecord[]; - index: Index; - osDomain: string; - transforms: any; - topicPartition: string; -}) => { - const docs: Array<(typeof transforms)[keyof typeof transforms]["Schema"]> = []; - for (const kafkaRecord of kafkaRecords) { - console.log(JSON.stringify(kafkaRecord, null, 2)); - const { value } = kafkaRecord; - try { - // If a legacy tombstone, handle and continue - // TODO: handle. for now, just continue - if (!value) { - // docs.push(opensearch.main.legacyPackageView.tombstone(id)); - continue; - } - - // Parse the kafka record's value - const record = JSON.parse(decodeBase64WithUtf8(value)); - - // If we're not a mako event, continue - // TODO: handle legacy. for now, just continue - if (!record.event || record?.origin !== "mako") { - continue; - } - - // If the event is a supported event, transform and push to docs array for indexing - if (record.event in transforms) { - const transformForEvent = transforms[record.event as keyof typeof transforms]; - - const result = transformForEvent.transform().safeParse(record); - - if (result.success && result.data === undefined) continue; - if (!result.success) { - logError({ - type: ErrorType.VALIDATION, - error: result?.error, - metadata: { topicPartition, kafkaRecord, record }, - }); - continue; - } - console.log(JSON.stringify(result.data, null, 2)); - docs.push(result.data); - } else { - console.log(`No transform found for event: ${record.event}`); - } - } catch (error) { - logError({ - type: ErrorType.BADPARSE, - error, - metadata: { topicPartition, kafkaRecord }, - }); - } - } + const prettifiedEventJSON = JSON.stringify(event, null, 2); - // Send all transformed records for indexing - await bulkUpdateDataWrapper(osDomain, index, docs); -}; - -const ksql = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { - const docs: any[] = []; - - // fetch the date for all kafkaRecords in the list from opensearch - const ids = kafkaRecords.map((record) => { - const decodedId = JSON.parse(decodeBase64WithUtf8(record.key)); - - return decodedId; - }); - - const openSearchRecords = await os.getItems(osDomain, indexNamespace, ids); - - const existingRecordsLookup = openSearchRecords.reduce>((acc, item) => { - const epochDate = new Date(item.changedDate).getTime(); // Convert `changedDate` to epoch number - acc[item.id] = epochDate; // Use `id` as the key and epoch date as the value - return acc; - }, {}); - - console.log("are we here 4"); + console.log(`event: ${prettifiedEventJSON}`); - for (const kafkaRecord of kafkaRecords) { - const { key, value } = kafkaRecord; - try { - const id: string = JSON.parse(decodeBase64WithUtf8(key)); - - // Handle deletes and continue - if (!value) { - docs.push(opensearch.main.seatool.tombstone(id)); - continue; - } + try { + await Promise.all( + Object.entries(event.records).map(async ([topicPartition, records]) => { + const topic = getTopic(topicPartition); - // Handle everything else and continue - const record = { - id, - ...JSON.parse(decodeBase64WithUtf8(value)), - }; - const result = opensearch.main.seatool.transform(id).safeParse(record); - if (!result.success) { - logError({ - type: ErrorType.VALIDATION, - error: result?.error, - metadata: { topicPartition, kafkaRecord, record }, - }); - continue; - } - console.log("--------------------"); - console.log(`id: ${result.data.id}`); - console.log(`mako: ` + existingRecordsLookup[result.data.id]); - console.log(`seatool: ` + result.data.changed_date); - if ( - existingRecordsLookup[result.data.id] && // Check if defined - (!result.data.changed_date || // Check if not defined or... - result.data.changed_date < existingRecordsLookup[result.data.id]) // ...less than existingRecordsLookup[result.data.id] - ) { - console.log(`SKIP`); - continue; - } - console.log(`INDEX`); - console.log("--------------------"); + console.log(`topic: ${topic}`); - if ( - result.data.authority && - typeof result.data.seatoolStatus === "string" && - result.data.seatoolStatus != "Unknown" - ) { - console.log("what status are we writing", JSON.stringify(result.data)); - docs.push({ ...result.data }); - } - } catch (error) { - logError({ - type: ErrorType.BADPARSE, - error, - metadata: { topicPartition, kafkaRecord }, - }); - } - } - await bulkUpdateDataWrapper(osDomain, index, docs); -}; + switch (topic) { + case "aws.onemac.migration.cdc": + return insertOneMacRecordsFromKafkaIntoMako(records, topicPartition); -const changed_date = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { - const docs: any[] = []; - for (const kafkaRecord of kafkaRecords) { - const { value } = kafkaRecord; - try { - // Handle delete events and continue - if (value === undefined) { - continue; - } + case "aws.seatool.ksql.onemac.three.agg.State_Plan": + return insertNewSeatoolRecordsFromKafkaIntoMako(records, topicPartition); - // Parse record - const decodedValue = Buffer.from(value, "base64").toString("utf-8"); - const record = JSON.parse(decodedValue).payload.after; + case "aws.seatool.debezium.changed_date.SEA.dbo.State_Plan": + return syncSeatoolRecordDatesFromKafkaWithMako(records, topicPartition); - // Handle tombstone events and continue - if (!record) continue; + default: + logError({ type: ErrorType.BADTOPIC }); + throw new Error(`topic (${topicPartition}) is invalid`); + } + }), + ); + } catch (error) { + logError({ type: ErrorType.UNKNOWN, metadata: { event: prettifiedEventJSON } }); - const result = opensearch.main.changedDate.transform().safeParse(record); - if (!result.success) { - logError({ - type: ErrorType.VALIDATION, - error: result?.error, - metadata: { topicPartition, kafkaRecord, record }, - }); - continue; - } - docs.push(result.data); - } catch (error) { - logError({ - type: ErrorType.BADPARSE, - error, - metadata: { topicPartition, kafkaRecord }, - }); - } + throw error; } - await bulkUpdateDataWrapper(osDomain, index, docs); }; diff --git a/lib/lambda/sinkMainProcessors.test.ts b/lib/lambda/sinkMainProcessors.test.ts new file mode 100644 index 0000000000..83e63c8b30 --- /dev/null +++ b/lib/lambda/sinkMainProcessors.test.ts @@ -0,0 +1,885 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + insertNewSeatoolRecordsFromKafkaIntoMako, + insertOneMacRecordsFromKafkaIntoMako, + syncSeatoolRecordDatesFromKafkaWithMako, +} from "./sinkMainProcessors"; +import * as sinkLib from "libs"; +import { Document, seatool } from "shared-types/opensearch/main"; +import { offsetToUtc } from "shared-utils"; +import { KafkaRecord } from "lib/packages/shared-types"; + +const convertObjToBase64 = (obj: object) => Buffer.from(JSON.stringify(obj)).toString("base64"); + +const createKafkaRecord = ({ + topic, + key, + value, +}: { + topic: string; + key: string; + value: string; +}): KafkaRecord => ({ + topic, + partition: 0, + offset: 0, + timestamp: 1732645041557, + timestampType: "CREATE_TIME", + headers: {}, + key, + value, +}); + +describe("insertOneMacRecordsFromKafkaIntoMako", () => { + const spiedOnBulkUpdateDataWrapper = vi.fn(); + const TOPIC = "--mako--branch-name--aws.onemac.migration.cdc"; + + beforeEach(() => { + vi.clearAllMocks(); + vi.useFakeTimers(); + + vi.spyOn(sinkLib, "bulkUpdateDataWrapper").mockImplementation(spiedOnBulkUpdateDataWrapper); + vi.stubEnv("osDomain", "osDomain"); + vi.stubEnv("indexNamespace", "indexNamespace"); + }); + + it("handles valid kafka records", () => { + insertOneMacRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ + event: "new-medicaid-submission", + attachments: { + cmsForm179: { + files: [ + { + filename: "Screenshot 2024-07-08 at 11.42.35 AM.png", + title: "Screenshot 2024-07-08 at 11.42.35 AM", + bucket: "mako-refactor-tests-sink-attachments-635052997545", + key: "13513eea-ba62-4cba-af31-2ec3c160b5e1.png", + uploadDate: 1732645033529, + }, + ], + label: "CMS Form 179", + }, + spaPages: { + files: [ + { + filename: "Screenshot 2024-07-08 at 11.42.35 AM.png", + title: "Screenshot 2024-07-08 at 11.42.35 AM", + bucket: "mako-refactor-tests-sink-attachments-635052997545", + key: "bbdfa95f-f67c-4983-8517-2745cc08d3b6.png", + uploadDate: 1732645038805, + }, + ], + label: "SPA Pages", + }, + coverLetter: { label: "Cover Letter" }, + tribalEngagement: { label: "Document Demonstrating Good-Faith Tribal Engagement" }, + existingStatePlanPages: { label: "Existing State Plan Page(s)" }, + publicNotice: { label: "Public Notice" }, + sfq: { label: "Standard Funding Questions (SFQs)" }, + tribalConsultation: { label: "Tribal Consultation" }, + other: { label: "Other" }, + }, + authority: "Medicaid SPA", + proposedEffectiveDate: 1732597200000, + id: "MD-24-2300", + origin: "mako", + submitterName: "George Harrison", + submitterEmail: "george@example.com", + timestamp: 1732645041526, + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith( + [ + { + additionalInformation: undefined, + authority: "Medicaid SPA", + changedDate: "2024-11-26T18:17:21.526Z", + cmsStatus: "Pending", + description: null, + id: "MD-24-2300", + makoChangedDate: "2024-11-26T18:17:21.526Z", + origin: "OneMAC", + raiWithdrawEnabled: false, + seatoolStatus: "Pending", + state: "MD", + stateStatus: "Under Review", + statusDate: offsetToUtc(new Date(1732645041526)).toISOString(), + proposedDate: 1732597200000, + subject: null, + submissionDate: "2024-11-26T00:00:00.000Z", + submitterEmail: "george@example.com", + submitterName: "George Harrison", + initialIntakeNeeded: true, + }, + ], + "main", + ); + }); + + it("skips value-less kafka records", () => { + insertOneMacRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: "", // <-- missing value + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("skips kafka records with invalid event name", () => { + insertOneMacRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + // encoded string with `invalid-event-name` as 'record.event` + value: convertObjToBase64({ + event: "invalid-event-name", + attachments: { + cmsForm179: { + files: [ + { + filename: "Screenshot 2024-07-08 at 11.42.35 AM.png", + title: "Screenshot 2024-07-08 at 11.42.35 AM", + bucket: "mako-refactor-tests-sink-attachments-635052997545", + key: "13513eea-ba62-4cba-af31-2ec3c160b5e1.png", + uploadDate: 1732645033529, + }, + ], + label: "CMS Form 179", + }, + spaPages: { + files: [ + { + filename: "Screenshot 2024-07-08 at 11.42.35 AM.png", + title: "Screenshot 2024-07-08 at 11.42.35 AM", + bucket: "mako-refactor-tests-sink-attachments-635052997545", + key: "bbdfa95f-f67c-4983-8517-2745cc08d3b6.png", + uploadDate: 1732645038805, + }, + ], + label: "SPA Pages", + }, + coverLetter: { label: "Cover Letter" }, + tribalEngagement: { label: "Document Demonstrating Good-Faith Tribal Engagement" }, + existingStatePlanPages: { label: "Existing State Plan Page(s)" }, + publicNotice: { label: "Public Notice" }, + sfq: { label: "Standard Funding Questions (SFQs)" }, + tribalConsultation: { label: "Tribal Consultation" }, + other: { label: "Other" }, + }, + authority: "Medicaid SPA", + proposedEffectiveDate: 1732597200000, + id: "MD-24-2300", + origin: "mako", + submitterName: "George Harrison", + submitterEmail: "george@example.com", + timestamp: 1732645041526, + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("skips kafka records with invalid properties", () => { + insertOneMacRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + // encoded string with `attachments` as an empty {} + value: convertObjToBase64({ + event: "new-medicaid-submission", + attachments: {}, + authority: "Medicaid SPA", + proposedEffectiveDate: 1732597200000, + id: "MD-24-2300", + origin: "mako", + submitterName: "George Harrison", + submitterEmail: "george@example.com", + timestamp: 1732645041526, + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("skips kafka records with invalid JSON", () => { + const spiedOnLogError = vi.spyOn(sinkLib, "logError").mockImplementation(vi.fn()); + + insertOneMacRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: "bunch-of-gibberish", + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + expect(spiedOnLogError).toBeCalledWith({ + type: "badparse", + error: expect.any(Object), + metadata: expect.any(Object), + }); + }); +}); + +describe("insertNewSeatoolRecordsFromKafkaIntoMako", () => { + // @ts-expect-error – cannot bother typing out unnecessary Document properties + const spiedOnGetItems = vi.fn<() => Promise>(() => + Promise.resolve([ + { + id: "MD-24-2300", + changedDate: "2024-02-04 23:12:36", + }, + { + id: "WA-22-2100", + changedDate: "2024-06-12 13:24:43", + }, + { + id: "NY-23-2200", + changedDate: "2024-10-12 09:04:52", + }, + { + id: "WV-24-3230", + changedDate: "2024-03-21 09:51:23", + }, + { + id: "IL-25-3130", + changedDate: "2025-03-21 17:51:23", + }, + ]), + ); + const spiedOnBulkUpdateDataWrapper = vi.fn(); + const spiedOnLogError = vi.fn(); + const TOPIC = "--mako--branch-name--aws.seatool.ksql.onemac.three.agg.State_Plan"; + + beforeEach(() => { + vi.clearAllMocks(); + + vi.spyOn(sinkLib, "getItems").mockImplementation(spiedOnGetItems); + vi.spyOn(sinkLib, "bulkUpdateDataWrapper").mockImplementation(spiedOnBulkUpdateDataWrapper); + vi.spyOn(sinkLib, "logError").mockImplementation(spiedOnLogError); + + vi.stubEnv("osDomain", "osDomain"); + vi.stubEnv("indexNamespace", "indexNamespace"); + }); + + it("outputs kafka records into mako records", async () => { + await insertNewSeatoolRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ + id: "MD-24-2300", + ACTION_OFFICERS: [ + { + FIRST_NAME: "John", + LAST_NAME: "Doe", + EMAIL: "john.doe@medicaid.gov", + OFFICER_ID: 12345, + DEPARTMENT: "State Plan Review", + PHONE: "202-555-1234", + }, + { + FIRST_NAME: "Emily", + LAST_NAME: "Rodriguez", + EMAIL: "emily.rodriguez@medicaid.gov", + OFFICER_ID: 12346, + DEPARTMENT: "Compliance Division", + PHONE: "202-555-5678", + }, + ], + LEAD_ANALYST: [ + { + FIRST_NAME: "Michael", + LAST_NAME: "Chen", + EMAIL: "michael.chen@cms.hhs.gov", + OFFICER_ID: 67890, + DEPARTMENT: "Medicaid Innovation Center", + PHONE: "202-555-9012", + }, + ], + STATE_PLAN: { + PLAN_TYPE: 123, + SPW_STATUS_ID: 4, + APPROVED_EFFECTIVE_DATE: 1707088356000, + CHANGED_DATE: 1704163200000, + SUMMARY_MEMO: "Sample summary", + TITLE_NAME: "Sample Title", + STATUS_DATE: 1704240000000, + SUBMISSION_DATE: 1704326400000, + LEAD_ANALYST_ID: 67890, + ACTUAL_EFFECTIVE_DATE: null, + PROPOSED_DATE: null, + STATE_CODE: "10", + }, + RAI: [], + ACTIONTYPES: [{ ACTION_NAME: "Initial Review", ACTION_ID: 1, PLAN_TYPE_ID: 123 }], + STATE_PLAN_SERVICETYPES: [{ SPA_TYPE_ID: 1, SPA_TYPE_NAME: "Type A" }], + STATE_PLAN_SERVICE_SUBTYPES: [{ TYPE_ID: 1, TYPE_NAME: "SubType X" }], + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith( + [ + { + actionType: "Initial Review", + approvedEffectiveDate: "2024-02-04T23:12:36.000Z", + authority: "1915(c)", + changed_date: 1704163200000, + cmsStatus: "Approved", + description: "Sample summary", + finalDispositionDate: "2024-01-03T00:00:00.000Z", + id: "MD-24-2300", + initialIntakeNeeded: false, + leadAnalystEmail: "michael.chen@cms.hhs.gov", + leadAnalystName: "Michael Chen", + leadAnalystOfficerId: 67890, + locked: false, + proposedDate: null, + raiReceivedDate: null, + raiRequestedDate: null, + raiWithdrawEnabled: false, + raiWithdrawnDate: null, + reviewTeam: [ + { + email: "john.doe@medicaid.gov", + name: "John Doe", + }, + { + email: "emily.rodriguez@medicaid.gov", + name: "Emily Rodriguez", + }, + ], + seatoolStatus: "Approved", + secondClock: false, + state: "10", + stateStatus: "Approved", + statusDate: "2024-01-03T00:00:00.000Z", + subTypes: [ + { + TYPE_ID: 1, + TYPE_NAME: "SubType X", + }, + ], + subject: "Sample Title", + submissionDate: "2024-01-04T00:00:00.000Z", + types: [ + { + SPA_TYPE_ID: 1, + SPA_TYPE_NAME: "Type A", + }, + ], + }, + ], + "main", + ); + }); + + it("skips newer mako records", async () => { + await insertNewSeatoolRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "V1YtMjQtMzIzMA==", + value: convertObjToBase64({ + id: "WV-24-3230", + ACTION_OFFICERS: [ + { + FIRST_NAME: "Lisa", + LAST_NAME: "Thompson", + EMAIL: "lisa.thompson@medicaid.gov", + OFFICER_ID: 78901, + DEPARTMENT: "Rural Health Services", + PHONE: "202-555-3456", + }, + { + FIRST_NAME: "Kevin", + LAST_NAME: "Anderson", + EMAIL: "kevin.anderson@medicaid.gov", + OFFICER_ID: 78902, + DEPARTMENT: "Financial Planning", + PHONE: "202-555-7890", + }, + ], + LEAD_ANALYST: [ + { + FIRST_NAME: "Elizabeth", + LAST_NAME: "Kim", + EMAIL: "elizabeth.kim@cms.hhs.gov", + OFFICER_ID: 23456, + DEPARTMENT: "Policy Integration", + PHONE: "202-555-2345", + }, + ], + STATE_PLAN: { + PLAN_TYPE: 121, + SPW_STATUS_ID: 11, + APPROVED_EFFECTIVE_DATE: 1711065600000, + CHANGED_DATE: 1711065600000, + SUMMARY_MEMO: "1115 Demonstration Waiver Review", + TITLE_NAME: "WV 1115 Medicaid Demonstration Project", + STATUS_DATE: 1710979200000, + SUBMISSION_DATE: 1711065600000, + LEAD_ANALYST_ID: 23456, + ACTUAL_EFFECTIVE_DATE: null, + PROPOSED_DATE: null, + STATE_CODE: "40", + }, + RAI: [], + ACTIONTYPES: [{ ACTION_NAME: "Pending Approval", ACTION_ID: 4, PLAN_TYPE_ID: 121 }], + STATE_PLAN_SERVICETYPES: [{ SPA_TYPE_ID: 4, SPA_TYPE_NAME: "Type D" }], + STATE_PLAN_SERVICE_SUBTYPES: [{ TYPE_ID: 4, TYPE_NAME: "SubType W" }], + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("tombstones records with no value property", async () => { + const spiedOnTombstone = vi.spyOn(seatool, "tombstone"); + + await insertNewSeatoolRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TlktMjMtMjIwMA==", + value: "", // <-- missing value + }), + ], + TOPIC, + ); + + expect(spiedOnTombstone).toBeCalledWith("NY-23-2200"); + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith( + [ + { + actionType: null, + approvedEffectiveDate: null, + authority: null, + changedDate: null, + cmsStatus: null, + description: null, + finalDispositionDate: null, + id: "NY-23-2200", + leadAnalystName: null, + leadAnalystOfficerId: null, + proposedDate: null, + raiReceivedDate: null, + raiRequestedDate: null, + raiWithdrawnDate: null, + reviewTeam: null, + seatoolStatus: null, + state: null, + stateStatus: null, + statusDate: null, + subTypes: null, + subject: null, + submissionDate: null, + types: null, + }, + ], + "main", + ); + }); + + it("skips over records with no key property", async () => { + await insertNewSeatoolRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "", // <-- missing key + value: convertObjToBase64({ + id: "IL-25-3130", + ACTION_OFFICERS: [ + { + FIRST_NAME: "Amanda", + LAST_NAME: "Brown", + EMAIL: "amanda.brown@medicaid.gov", + OFFICER_ID: 89012, + DEPARTMENT: "Strategic Initiatives", + PHONE: "202-555-5678", + }, + { + FIRST_NAME: "Carlos", + LAST_NAME: "Mendez", + EMAIL: "carlos.mendez@medicaid.gov", + OFFICER_ID: 89013, + DEPARTMENT: "Program Evaluation", + PHONE: "202-555-8901", + }, + ], + LEAD_ANALYST: [ + { + FIRST_NAME: "Daniel", + LAST_NAME: "Park", + EMAIL: "daniel.park@cms.hhs.gov", + OFFICER_ID: 34567, + DEPARTMENT: "Innovative Solutions", + PHONE: "202-555-3456", + }, + ], + STATE_PLAN: { + PLAN_TYPE: 124, + SPW_STATUS_ID: 10, + APPROVED_EFFECTIVE_DATE: 1740624000000, + CHANGED_DATE: 1740624000000, + SUMMARY_MEMO: "CHIP State Plan Amendment Review", + TITLE_NAME: "IL CHIP Program Financial Strategy", + STATUS_DATE: 1740537600000, + SUBMISSION_DATE: 1740624000000, + LEAD_ANALYST_ID: 34567, + ACTUAL_EFFECTIVE_DATE: null, + PROPOSED_DATE: null, + STATE_CODE: "50", + }, + RAI: [], + ACTIONTYPES: [{ ACTION_NAME: "Financial Review", ACTION_ID: 5, PLAN_TYPE_ID: 124 }], + STATE_PLAN_SERVICETYPES: [{ SPA_TYPE_ID: 5, SPA_TYPE_NAME: "Type E" }], + STATE_PLAN_SERVICE_SUBTYPES: [{ TYPE_ID: 5, TYPE_NAME: "SubType V" }], + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("skips over records with invalid properties", async () => { + await insertNewSeatoolRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "V1YtMjQtMzIzMA==", + // value is invalid JSON + value: "bunch-of-gibberish", + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + expect(spiedOnLogError).toBeCalledWith({ + type: "badparse", + metadata: expect.any(Object), + error: expect.any(Object), + }); + }); + + it("skips over records with seatoolStatus:'Unknown' || authority: null property values", async () => { + await insertNewSeatoolRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ + id: "MD-24-2300", + ACTION_OFFICERS: [ + { + FIRST_NAME: "John", + LAST_NAME: "Doe", + EMAIL: "john.doe@medicaid.gov", + OFFICER_ID: 12345, + DEPARTMENT: "State Plan Review", + PHONE: "202-555-1234", + }, + { + FIRST_NAME: "Emily", + LAST_NAME: "Rodriguez", + EMAIL: "emily.rodriguez@medicaid.gov", + OFFICER_ID: 12346, + DEPARTMENT: "Compliance Division", + PHONE: "202-555-5678", + }, + ], + LEAD_ANALYST: [ + { + FIRST_NAME: "Michael", + LAST_NAME: "Chen", + EMAIL: "michael.chen@cms.hhs.gov", + OFFICER_ID: 67890, + DEPARTMENT: "Medicaid Innovation Center", + PHONE: "202-555-9012", + }, + ], + STATE_PLAN: { + PLAN_TYPE: 999, + SPW_STATUS_ID: 999, + APPROVED_EFFECTIVE_DATE: 1707088356000, + CHANGED_DATE: 1704163200000, + SUMMARY_MEMO: "Sample summary", + TITLE_NAME: "Sample Title", + STATUS_DATE: 1704240000000, + SUBMISSION_DATE: 1704326400000, + LEAD_ANALYST_ID: 67890, + ACTUAL_EFFECTIVE_DATE: null, + PROPOSED_DATE: null, + STATE_CODE: "10", + }, + RAI: [], + ACTIONTYPES: [{ ACTION_NAME: "Initial Review", ACTION_ID: 1, PLAN_TYPE_ID: 123 }], + STATE_PLAN_SERVICETYPES: [{ SPA_TYPE_ID: 1, SPA_TYPE_NAME: "Type A" }], + STATE_PLAN_SERVICE_SUBTYPES: [{ TYPE_ID: 1, TYPE_NAME: "SubType X" }], + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("skips over records that fail SEATOOL safeParse", async () => { + await insertNewSeatoolRecordsFromKafkaIntoMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ + ACTION_OFFICERS: [ + { + FIRST_NAME: "John", + LAST_NAME: "Doe", + EMAIL: "john.doe@medicaid.gov", + OFFICER_ID: 12345, + DEPARTMENT: "State Plan Review", + PHONE: "202-555-1234", + }, + { + FIRST_NAME: "Emily", + LAST_NAME: "Rodriguez", + EMAIL: "emily.rodriguez@medicaid.gov", + OFFICER_ID: 12346, + DEPARTMENT: "Compliance Division", + PHONE: "202-555-5678", + }, + ], + LEAD_ANALYST: [ + { + FIRST_NAME: "Michael", + LAST_NAME: "Chen", + EMAIL: "michael.chen@cms.hhs.gov", + OFFICER_ID: 67890, + DEPARTMENT: "Medicaid Innovation Center", + PHONE: "202-555-9012", + }, + ], + STATE_PLAN: { + PLAN_TYPE: 123, + SPW_STATUS_ID: 4, + APPROVED_EFFECTIVE_DATE: 1707088356000, + CHANGED_DATE: 1704163200000, + SUMMARY_MEMO: "Sample summary", + TITLE_NAME: "Sample Title", + STATUS_DATE: 1704240000000, + SUBMISSION_DATE: 1704326400000, + LEAD_ANALYST_ID: 67890, + ACTUAL_EFFECTIVE_DATE: null, + PROPOSED_DATE: null, + STATE_CODE: "10", + }, + RAI: [], + ACTIONTYPES: [{ ACTION_NAME: "Initial Review", ACTION_ID: 1 }], + STATE_PLAN_SERVICETYPES: [{ SPA_TYPE_ID: 1, SPA_TYPE_NAME: "Type A" }], + STATE_PLAN_SERVICE_SUBTYPES: [{ TYPE_ID: 1, TYPE_NAME: "SubType X" }], + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + expect(spiedOnLogError).toBeCalledWith({ + type: "validation", + metadata: expect.any(Object), + error: expect.any(Array), + }); + }); +}); + +describe("syncSeatoolRecordDatesFromKafkaWithMako", () => { + const spiedOnBulkUpdateDataWrapper = vi.fn(); + const spiedOnLogError = vi.fn(); + + const TOPIC = "--mako--branch-name--aws.seatool.debezium.changed_date.SEA.dbo.State_Plan"; + + beforeEach(() => { + vi.resetAllMocks(); + + vi.spyOn(sinkLib, "bulkUpdateDataWrapper").mockImplementation(spiedOnBulkUpdateDataWrapper); + vi.spyOn(sinkLib, "logError").mockImplementation(spiedOnLogError); + + vi.stubEnv("osDomain", "osDomain"); + vi.stubEnv("indexNamespace", "indexNamespace"); + }); + + it("processes a valid date change to mako", async () => { + await syncSeatoolRecordDatesFromKafkaWithMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ + payload: { after: { ID_Number: "12345", Changed_Date: 1672531200000 } }, + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith( + [ + { + changedDate: "2023-01-01T00:00:00.000Z", + id: "12345", + }, + ], + "main", + ); + }); + + it("processes a date change that's null to mako", async () => { + await syncSeatoolRecordDatesFromKafkaWithMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ + payload: { after: { ID_Number: "67890", Changed_Date: null } }, + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith( + [ + { + changedDate: null, + id: "67890", + }, + ], + "main", + ); + }); + + it("skips records with no value property", async () => { + await syncSeatoolRecordDatesFromKafkaWithMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: "", + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("skips payloads that're null or undefined", async () => { + await syncSeatoolRecordDatesFromKafkaWithMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ payload: { after: null } }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + + await syncSeatoolRecordDatesFromKafkaWithMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: convertObjToBase64({ + payload: { + after: undefined, + }, + }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + }); + + it("skips payloads with missing Id property", async () => { + await syncSeatoolRecordDatesFromKafkaWithMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + // missing required Id property + value: convertObjToBase64({ payload: { after: { Changed_Date: 1672531200000 } } }), + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + expect(spiedOnLogError).toBeCalledWith({ + type: "validation", + error: expect.any(Object), + metadata: expect.any(Object), + }); + }); + + it("skips payloads with invalid JSON", async () => { + await syncSeatoolRecordDatesFromKafkaWithMako( + [ + createKafkaRecord({ + topic: TOPIC, + key: "TUQtMjQtMjMwMA==", + value: "bunch-of-gibberish", + }), + ], + TOPIC, + ); + + expect(spiedOnBulkUpdateDataWrapper).toBeCalledWith([], "main"); + expect(spiedOnLogError).toBeCalledWith({ + type: "badparse", + error: expect.any(Object), + metadata: expect.any(Object), + }); + }); +}); diff --git a/lib/lambda/sinkMainProcessors.ts b/lib/lambda/sinkMainProcessors.ts new file mode 100644 index 0000000000..3615a3cf06 --- /dev/null +++ b/lib/lambda/sinkMainProcessors.ts @@ -0,0 +1,266 @@ +import { bulkUpdateDataWrapper, ErrorType, logError, getItems } from "libs"; +import { KafkaRecord, opensearch, SeatoolRecordWithUpdatedDate } from "shared-types"; +import { Document, transforms } from "shared-types/opensearch/main"; +import { decodeBase64WithUtf8 } from "shared-utils"; +import { isBefore } from "date-fns"; + +const removeDoubleQuotesSurroundingString = (str: string) => str.replace(/^"|"$/g, ""); + +type OneMacRecord = { + id: string; + makoChangedDate: string | null; +}; + +const isRecordAOneMacRecord = ( + record: Partial<{ + event: string; + origin: string; + }>, +): record is { event: keyof typeof transforms } => + typeof record === "object" && + record?.event !== undefined && + record.event in transforms && + record?.origin === "mako"; + +const getOneMacRecordWithAllProperties = ( + value: string, + topicPartition: string, + kafkaRecord: KafkaRecord, +): OneMacRecord | undefined => { + const record = JSON.parse(decodeBase64WithUtf8(value)); + + if (isRecordAOneMacRecord(record)) { + const transformForEvent = transforms[record.event]; + + const safeEvent = transformForEvent.transform().safeParse(record); + + if (safeEvent.success === false) { + logError({ + type: ErrorType.VALIDATION, + error: safeEvent.error, + metadata: { topicPartition, kafkaRecord, record }, + }); + + return; + } + + const { data: oneMacRecord } = safeEvent; + + console.log(`event after transformation: ${JSON.stringify(oneMacRecord, null, 2)}`); + + return oneMacRecord; + } else { + console.log(`No transform found for event: ${record.event}`); + } + + return; +}; + +/** + * Processes incoming new records from the OneMac user interface and adds them to Mako + * @param kafkaRecords records to process + * @param topicPartition kafka topic for verbose error handling + */ +export const insertOneMacRecordsFromKafkaIntoMako = async ( + kafkaRecords: KafkaRecord[], + topicPartition: string, +) => { + const oneMacRecordsForMako = kafkaRecords.reduce((collection, kafkaRecord) => { + console.log(`record: ${JSON.stringify(kafkaRecord, null, 2)}`); + + try { + const { value } = kafkaRecord; + + if (!value) { + return collection; + } + + const oneMacRecordWithAllProperties = getOneMacRecordWithAllProperties( + value, + topicPartition, + kafkaRecord, + ); + + if (oneMacRecordWithAllProperties) { + return collection.concat(oneMacRecordWithAllProperties); + } + } catch (error) { + logError({ + type: ErrorType.BADPARSE, + error, + metadata: { topicPartition, kafkaRecord }, + }); + } + + return collection; + }, []); + + await bulkUpdateDataWrapper(oneMacRecordsForMako, "main"); +}; + +const getMakoDocTimestamps = async (kafkaRecords: KafkaRecord[]) => { + const kafkaIds = kafkaRecords.map((record) => + removeDoubleQuotesSurroundingString(decodeBase64WithUtf8(record.key)), + ); + const openSearchRecords = await getItems(kafkaIds); + + return openSearchRecords.reduce>((map, item) => { + if (item.changedDate !== null) { + map.set(item.id, new Date(item.changedDate).getTime()); + } + + return map; + }, new Map()); +}; + +/** + * Processes new SEATOOL records and reconciles them with existing Mako records + * @param kafkaRecords records to process + * @param topicPartition kafka topic for verbose error handling + */ +export const insertNewSeatoolRecordsFromKafkaIntoMako = async ( + kafkaRecords: KafkaRecord[], + topicPartition: string, +) => { + const makoDocTimestamps = await getMakoDocTimestamps(kafkaRecords); + + const seatoolRecordsForMako = kafkaRecords.reduce<{ id: string; [key: string]: unknown }[]>( + (collection, kafkaRecord) => { + try { + const { key, value } = kafkaRecord; + + if (!key) { + console.log(`Record without a key property: ${value}`); + + return collection; + } + + const id: string = removeDoubleQuotesSurroundingString(decodeBase64WithUtf8(key)); + + if (!value) { + // record in seatool has been deleted + // nulls the seatool properties from the record + // seatool record would now only have mako properties + console.log(`Record without a value property: ${value}`); + return collection.concat(opensearch.main.seatool.tombstone(id)); + } + + const seatoolRecord: Document = { + id, + ...JSON.parse(decodeBase64WithUtf8(value)), + }; + + const safeSeatoolRecord = opensearch.main.seatool.transform(id).safeParse(seatoolRecord); + + if (safeSeatoolRecord.success === false) { + logError({ + type: ErrorType.VALIDATION, + error: safeSeatoolRecord.error.errors, + metadata: { topicPartition, kafkaRecord, record: seatoolRecord }, + }); + + return collection; + } + + const { data: seatoolDocument } = safeSeatoolRecord; + const makoDocumentTimestamp = makoDocTimestamps.get(seatoolDocument.id); + + console.log("--------------------"); + console.log(`id: ${seatoolDocument.id}`); + console.log(`mako: ${makoDocumentTimestamp}`); + console.log(`seatool: ${seatoolDocument.changed_date}`); + + const isNewerOrUndefined = + seatoolDocument.changed_date && + makoDocumentTimestamp && + isBefore(makoDocumentTimestamp, seatoolDocument.changed_date); + + if (isNewerOrUndefined) { + console.log("SKIPPED DUE TO OUT-OF-DATE INFORMATION"); + return collection; + } + + if (seatoolDocument.authority && seatoolDocument.seatoolStatus !== "Unknown") { + console.log("INDEX"); + console.log("--------------------"); + console.log(`Status: ${seatoolDocument.seatoolStatus}`); + + return collection.concat({ ...seatoolDocument }); + } + } catch (error) { + logError({ + type: ErrorType.BADPARSE, + error, + metadata: { topicPartition, kafkaRecord }, + }); + } + + return collection; + }, + [], + ); + + await bulkUpdateDataWrapper(seatoolRecordsForMako, "main"); +}; + +/** + * Syncs date updates in SEATOOL records with Mako, offloading processing from `insertNewSeatoolRecordsFromKafkaIntoMako` + * @param kafkaRecords records with updated date payload + * @param topicPartition kafka topic for verbose error handling + */ +export const syncSeatoolRecordDatesFromKafkaWithMako = async ( + kafkaRecords: KafkaRecord[], + topicPartition: string, +) => { + const recordIdsWithUpdatedDates = kafkaRecords.reduce< + { id: string; changedDate: string | null }[] + >((collection, kafkaRecord) => { + const { value } = kafkaRecord; + + try { + if (!value) { + console.log(`Record without a value property: ${value}`); + + return collection; + } + + const payloadWithUpdatedDate: { payload?: { after?: SeatoolRecordWithUpdatedDate | null } } = + JSON.parse(decodeBase64WithUtf8(value)); + + // .after could be `null` or `undefined` + if (!payloadWithUpdatedDate?.payload?.after) { + return collection; + } + + const { after: recordWithUpdatedDate } = payloadWithUpdatedDate.payload; + + const safeRecordWithIdAndUpdatedDate = opensearch.main.changedDate + .transform() + .safeParse(recordWithUpdatedDate); + + if (safeRecordWithIdAndUpdatedDate.success === false) { + logError({ + type: ErrorType.VALIDATION, + error: safeRecordWithIdAndUpdatedDate.error.errors, + metadata: { topicPartition, kafkaRecord, recordWithUpdatedDate }, + }); + + return collection; + } + + const { data: idAndUpdatedDate } = safeRecordWithIdAndUpdatedDate; + + return collection.concat(idAndUpdatedDate); + } catch (error) { + logError({ + type: ErrorType.BADPARSE, + error, + metadata: { topicPartition, kafkaRecord }, + }); + } + + return collection; + }, []); + + await bulkUpdateDataWrapper(recordIdsWithUpdatedDates, "main"); +}; diff --git a/lib/lambda/sinkSubtypes.ts b/lib/lambda/sinkSubtypes.ts index 7d93a13021..ed460ec4f4 100644 --- a/lib/lambda/sinkSubtypes.ts +++ b/lib/lambda/sinkSubtypes.ts @@ -1,18 +1,7 @@ import { Handler } from "aws-lambda"; import { KafkaRecord, opensearch } from "shared-types"; import { KafkaEvent } from "shared-types"; -import { - ErrorType, - bulkUpdateDataWrapper, - getTopic, - logError, -} from "../libs/sink-lib"; -import { Index } from "shared-types/opensearch"; -const osDomain = process.env.osDomain; -if (!osDomain) { - throw new Error("Missing required environment variable(s)"); -} -const index: Index = `${process.env.indexNamespace}subtypes`; +import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; export const handler: Handler = async (event) => { const loggableEvent = { ...event, records: "too large to display" }; @@ -35,10 +24,7 @@ export const handler: Handler = async (event) => { } }; -const subtypes = async ( - kafkaRecords: KafkaRecord[], - topicPartition: string, -) => { +const subtypes = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { const docs: any[] = []; for (const kafkaRecord of kafkaRecords) { const { value } = kafkaRecord; @@ -66,5 +52,5 @@ const subtypes = async ( }); } } - await bulkUpdateDataWrapper(osDomain, index, docs); + await bulkUpdateDataWrapper(docs, "subtypes"); }; diff --git a/lib/lambda/sinkTypes.ts b/lib/lambda/sinkTypes.ts index 2c1d903359..922bb476b8 100644 --- a/lib/lambda/sinkTypes.ts +++ b/lib/lambda/sinkTypes.ts @@ -1,18 +1,7 @@ import { Handler } from "aws-lambda"; import { KafkaRecord, opensearch } from "shared-types"; import { KafkaEvent } from "shared-types"; -import { - ErrorType, - bulkUpdateDataWrapper, - getTopic, - logError, -} from "../libs/sink-lib"; -import { Index } from "shared-types/opensearch"; -const osDomain = process.env.osDomain; -if (!osDomain) { - throw new Error("Missing required environment variable(s)"); -} -const index: Index = `${process.env.indexNamespace}types`; +import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; export const handler: Handler = async (event) => { const loggableEvent = { ...event, records: "too large to display" }; @@ -63,5 +52,5 @@ const types = async (kafkaRecords: KafkaRecord[], topicPartition: string) => { }); } } - await bulkUpdateDataWrapper(osDomain, index, docs); + await bulkUpdateDataWrapper(docs, "types"); }; diff --git a/lib/lambda/utils.ts b/lib/lambda/utils.ts new file mode 100644 index 0000000000..683a4fa0a0 --- /dev/null +++ b/lib/lambda/utils.ts @@ -0,0 +1,27 @@ +import { errors as OpensearchErrors } from "@opensearch-project/opensearch"; + +export type ErrorResponse = { + statusCode: number; + body: { + error?: string; + message?: string; + } +} + +export const handleOpensearchError = (error: unknown): ErrorResponse => { + console.error({ error }); + if (error instanceof OpensearchErrors.ResponseError) { + return { + statusCode: error.statusCode || error.meta?.statusCode || 500, + body: { + error: error.body || error.meta?.body || error, + message: error.message, + } + }; + } + + return { + statusCode: 500, + body: { message: "Internal server error" }, + }; +} diff --git a/lib/libs/api/auth/user.test.ts b/lib/libs/api/auth/user.test.ts index 72a1b9afd7..5bdd749b62 100644 --- a/lib/libs/api/auth/user.test.ts +++ b/lib/libs/api/auth/user.test.ts @@ -1,276 +1,152 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; +import { APIGatewayEvent } from "aws-lambda"; +import { + convertUserAttributes, + getRequestContext, + makoReviewer, + makoStateSubmitter, + noStateSubmitter, + setMockUsername, + USER_POOL_ID, +} from "mocks"; +import { afterAll, describe, expect, it } from "vitest"; import { getAuthDetails, - lookupUserAttributes, + getStateFilter, isAuthorized, isAuthorizedToGetPackageActions, - getStateFilter, + lookupUserAttributes, } from "./user"; -import { APIGatewayEvent } from "aws-lambda"; -import { - CognitoIdentityProviderClient, - UserType as CognitoUserType, -} from "@aws-sdk/client-cognito-identity-provider"; - -vi.mock("@aws-sdk/client-cognito-identity-provider", () => { - const send = vi.fn(); - return { - CognitoIdentityProviderClient: vi.fn(() => ({ - send, - })), - ListUsersCommand: vi.fn(), - }; -}); describe("Auth functions", () => { - let event: APIGatewayEvent; - let mockSend: ReturnType; - - beforeEach(() => { - event = { - requestContext: { - identity: { - cognitoAuthenticationProvider: - "cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123,cognito-idp.us-east-1.amazonaws.com/us-east-1_ABC123:CognitoSignIn:12345678-1234-1234-1234-123456789012", - }, - }, - } as unknown as APIGatewayEvent; - - const client = new CognitoIdentityProviderClient({}); - mockSend = vi.mocked(client.send); + afterAll(() => { + setMockUsername(makoStateSubmitter); }); describe("getAuthDetails", () => { it("should return the correct auth details", () => { - const authDetails = getAuthDetails(event); + const authDetails = getAuthDetails({ + requestContext: getRequestContext(makoStateSubmitter), + } as APIGatewayEvent); expect(authDetails).toEqual({ - userId: "12345678-1234-1234-1234-123456789012", - poolId: "us-east-1_ABC123", + userId: makoStateSubmitter.Username, + poolId: USER_POOL_ID, }); }); it("should throw an error if no auth provider is present", () => { - event.requestContext.identity.cognitoAuthenticationProvider = ""; - expect(() => getAuthDetails(event)).toThrow("No auth provider!"); + expect(() => + getAuthDetails({ + requestContext: { + identity: {}, + }, + } as APIGatewayEvent), + ).toThrow("No auth provider!"); }); }); describe("lookupUserAttributes", () => { it("should return user attributes", async () => { - const mockUser = { - Username: "testuser", - Attributes: [ - { Name: "sub", Value: "12345678-1234-1234-1234-123456789012" }, - { Name: "custom:state", Value: "NY" }, - ], - } as CognitoUserType; - mockSend.mockResolvedValueOnce({ - Users: [mockUser], - }); - const userAttributes = await lookupUserAttributes( - "12345678-1234-1234-1234-123456789012", - "us-east-1_ABC123", + makoStateSubmitter.Username || "", + USER_POOL_ID, ); - expect(userAttributes).toEqual({ - sub: "12345678-1234-1234-1234-123456789012", - "custom:state": "NY", - username: "testuser", - }); + expect(userAttributes).toEqual(convertUserAttributes(makoStateSubmitter)); }); it("should throw an error if no user is found", async () => { - mockSend.mockResolvedValueOnce({ - Users: [], - }); - await expect( - lookupUserAttributes( - "12345678-1234-1234-1234-123456789012", - "us-east-1_ABC123", - ), + lookupUserAttributes("12345678-1234-1234-1234-123456789012", USER_POOL_ID), ).rejects.toThrow("No user found with this sub"); }); }); describe("isAuthorized", () => { it("should return true for a CMS user", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { Name: "custom:cms-roles", Value: "onemac-micro-reviewer" }, - ], - }, - ], - }); - - const result = await isAuthorized(event); + const result = await isAuthorized({ + requestContext: getRequestContext(makoReviewer), + } as APIGatewayEvent); expect(result).toBe(true); }); it("should return true for a state user with matching stateCode", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { - Name: "custom:cms-roles", - Value: "onemac-micro-statesubmitter", - }, - { - Name: "custom:state", - Value: "NY", - }, - ], - }, - ], - }); - - const result = await isAuthorized(event, "NY"); + const result = await isAuthorized( + { + requestContext: getRequestContext(makoStateSubmitter), + } as APIGatewayEvent, + "MD", + ); expect(result).toBe(true); }); it("should return false for a state user without matching stateCode", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { - Name: "custom:cms-roles", - Value: "onemac-micro-statesubmitter", - }, - { - Name: "custom:state", - Value: "NY", - }, - ], - }, - ], - }); - - const result = await isAuthorized(event, "CA"); + const result = await isAuthorized( + { + requestContext: getRequestContext(makoStateSubmitter), + } as APIGatewayEvent, + "CA", + ); expect(result).toBe(false); }); }); describe("isAuthorizedToGetPackageActions", () => { it("should return true for a CMS write user", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { Name: "custom:cms-roles", Value: "onemac-micro-reviewer" }, - ], - }, - ], - }); - - const result = await isAuthorizedToGetPackageActions(event); + const result = await isAuthorizedToGetPackageActions({ + requestContext: getRequestContext(makoReviewer), + } as APIGatewayEvent); expect(result).toBe(true); }); it("should return true for a state user with matching stateCode", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { - Name: "custom:cms-roles", - Value: "onemac-micro-statesubmitter", - }, - { - Name: "custom:state", - Value: "NY", - }, - ], - }, - ], - }); - - const result = await isAuthorizedToGetPackageActions(event, "NY"); + const result = await isAuthorizedToGetPackageActions( + { + requestContext: getRequestContext(makoStateSubmitter), + } as APIGatewayEvent, + "MD", + ); expect(result).toBe(true); }); it("should return false for a state user without matching stateCode", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { - Name: "custom:cms-roles", - Value: "onemac-micro-statesubmitter", - }, - { - Name: "custom:state", - Value: "NY", - }, - ], - }, - ], - }); - - const result = await isAuthorizedToGetPackageActions(event, "CA"); + const result = await isAuthorizedToGetPackageActions( + { + requestContext: getRequestContext(makoStateSubmitter), + } as APIGatewayEvent, + "CA", + ); expect(result).toBe(false); }); }); describe("getStateFilter", () => { it("should return state filter for a state user", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { - Name: "custom:cms-roles", - Value: "onemac-micro-statesubmitter", - }, - { Name: "custom:state", Value: "NY,CA" }, - ], - }, - ], - }); - - const result = await getStateFilter(event); + const stateString = + makoStateSubmitter.UserAttributes?.find((attr) => attr?.Name == "custom:state")?.Value || + ""; + const states = stateString.split(",").map((state) => state.toLocaleLowerCase()); + + const result = await getStateFilter({ + requestContext: getRequestContext(makoStateSubmitter), + } as APIGatewayEvent); expect(result).toEqual({ terms: { - state: ["ny", "ca"], + state: states, }, }); }); it("should throw an error if state user has no associated states", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { - Name: "custom:cms-roles", - Value: "onemac-micro-statesubmitter", - }, - { Name: "custom:state", Value: "" }, - ], - }, - ], - }); - - await expect(getStateFilter(event)).rejects.toThrow( - "State user detected, but no associated states. Cannot continue", - ); + await expect( + getStateFilter({ + requestContext: getRequestContext(noStateSubmitter), + } as APIGatewayEvent), + ).rejects.toThrow("State user detected, but no associated states. Cannot continue"); }); it("should return null for a CMS user", async () => { - mockSend.mockResolvedValueOnce({ - Users: [ - { - Attributes: [ - { Name: "custom:cms-roles", Value: "onemac-micro-reviewer" }, - ], - }, - ], - }); - - const result = await getStateFilter(event); + const result = await getStateFilter({ + requestContext: getRequestContext(makoReviewer), + } as APIGatewayEvent); expect(result).toBeNull(); }); }); diff --git a/lib/libs/api/package/changelog.test.ts b/lib/libs/api/package/changelog.test.ts index b9b19d3251..ebe2ded8ee 100644 --- a/lib/libs/api/package/changelog.test.ts +++ b/lib/libs/api/package/changelog.test.ts @@ -1,88 +1,34 @@ // getPackageChangelog.test.ts -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import * as os from "libs/opensearch-lib"; +import { TEST_ITEM_ID, WITHDRAWN_CHANGELOG_ITEM_ID } from "mocks"; +import items from "mocks/data/items"; +import { describe, expect, it } from "vitest"; import { getPackageChangelog } from "./changelog"; -import { opensearch } from "shared-types"; - -vi.mock("libs/opensearch-lib"); describe("getPackageChangelog", () => { - const mockOsDomain = "mock-os-domain"; - const mockIndexNamespace = "mock-index-namespace"; - const mockPackageId = "mock-package-id"; - const mockFilter = [{ term: { status: "active" } }]; - const mockResponse = { - hits: { - hits: [ - { - _source: { - timestamp: "2024-01-01T00:00:00Z", - change: "Initial release", - }, - }, - ], - }, - } as opensearch.changelog.Response; - - beforeEach(() => { - process.env.osDomain = mockOsDomain; - process.env.indexNamespace = mockIndexNamespace; - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - it("should throw an error if osDomain is not defined", async () => { delete process.env.osDomain; - await expect(getPackageChangelog(mockPackageId)).rejects.toThrow( + await expect(getPackageChangelog(TEST_ITEM_ID)).rejects.toThrow( "process.env.osDomain must be defined", ); }); it("should return the changelog with the specified packageId and no additional filters", async () => { - vi.mocked(os.search).mockResolvedValue(mockResponse); + const expectedHits = items[WITHDRAWN_CHANGELOG_ITEM_ID]?._source?.changelog || []; + const result = await getPackageChangelog(WITHDRAWN_CHANGELOG_ITEM_ID); - const result = await getPackageChangelog(mockPackageId); - - expect(os.search).toHaveBeenCalledWith( - mockOsDomain, - `${mockIndexNamespace}changelog`, - { - from: 0, - size: 200, - sort: [{ timestamp: "desc" }], - query: { - bool: { - must: [{ term: { "packageId.keyword": mockPackageId } }], - }, - }, - }, - ); - expect(result).toEqual(mockResponse); + expect(result).toBeTruthy(); + expect(result.hits.hits).toEqual(expectedHits); }); it("should return the changelog with the specified packageId and additional filters", async () => { - vi.mocked(os.search).mockResolvedValue(mockResponse); - - const result = await getPackageChangelog(mockPackageId, mockFilter); - - expect(os.search).toHaveBeenCalledWith( - mockOsDomain, - `${mockIndexNamespace}changelog`, - { - from: 0, - size: 200, - sort: [{ timestamp: "desc" }], - query: { - bool: { - must: [{ term: { "packageId.keyword": mockPackageId } }].concat( - mockFilter, - ), - }, - }, - }, + const expectedHit = items[WITHDRAWN_CHANGELOG_ITEM_ID]?._source?.changelog?.find( + (log) => log?._source?.event == "withdraw-package", ); - expect(result).toEqual(mockResponse); + const result = await getPackageChangelog(WITHDRAWN_CHANGELOG_ITEM_ID, [ + { term: { event: "withdraw-package" } }, + ]); + + expect(result).toBeTruthy(); + expect(result.hits.hits).toEqual([expectedHit]); }); }); diff --git a/lib/libs/api/package/getPackage.test.ts b/lib/libs/api/package/getPackage.test.ts index 0b01d55596..e7c175b513 100644 --- a/lib/libs/api/package/getPackage.test.ts +++ b/lib/libs/api/package/getPackage.test.ts @@ -1,85 +1,28 @@ // getPackage.test.ts -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import * as os from "libs/opensearch-lib"; -import { getPackage, ExtendedItemResult } from "./getPackage"; -import { getAppkChildren } from "./appk"; - -vi.mock("libs/opensearch-lib"); -vi.mock("./appk"); +import { INITIAL_RELEASE_APPK_ITEM_ID, TEST_ITEM_ID } from "mocks"; +import items from "mocks/data/items"; +import { describe, expect, it } from "vitest"; +import { getPackage } from "./getPackage"; describe("getPackage", () => { - const mockOsDomain = "mock-os-domain"; - const mockIndexNamespace = "mock-index-namespace"; - const mockId = "mock-id"; - - beforeEach(() => { - process.env.osDomain = mockOsDomain; - process.env.indexNamespace = mockIndexNamespace; - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - it("should throw an error if osDomain is not defined", async () => { delete process.env.osDomain; - await expect(getPackage(mockId)).rejects.toThrow( - "process.env.osDomain must be defined", - ); + await expect(getPackage(TEST_ITEM_ID)).rejects.toThrow("process.env.osDomain must be defined"); }); it("should return the package result without appkChildren if appkParent is not present", async () => { - const mockPackageResult = { - _id: mockId, - _source: { - someField: "someValue", - }, - } as ExtendedItemResult; + const result = await getPackage(TEST_ITEM_ID); - vi.mocked(os.getItem).mockResolvedValue(mockPackageResult); - - const result = await getPackage(mockId); - - expect(os.getItem).toHaveBeenCalledWith( - mockOsDomain, - `${mockIndexNamespace}main`, - mockId, - ); - expect(result).toEqual(mockPackageResult); + expect(result).not.toBeNull(); + expect(result).toEqual(items[TEST_ITEM_ID]); + expect(result?._source).not.toHaveProperty("appkChildren"); }); - it.skip("should return the package result with appkChildren if appkParent is present", async () => { - const mockPackageResult = { - _id: mockId, - _source: { - appkParent: "someParent", - someField: "someValue", - }, - } as ExtendedItemResult; - - const mockChildren = { - hits: { - hits: [{ childField: "childValue" }], - }, - }; - - vi.mocked(os.getItem).mockResolvedValue(mockPackageResult); - vi.mocked(getAppkChildren).mockResolvedValue(mockChildren); - - const result = await getPackage(mockId); + it("should return the package result with appkChildren if appkParent is present", async () => { + const result = await getPackage(INITIAL_RELEASE_APPK_ITEM_ID); - expect(os.getItem).toHaveBeenCalledWith( - mockOsDomain, - `${mockIndexNamespace}main`, - mockId, - ); - expect(getAppkChildren).toHaveBeenCalledWith(mockId); - expect(result).toEqual({ - ...mockPackageResult, - _source: { - ...mockPackageResult._source, - appkChildren: mockChildren.hits.hits, - }, - }); + expect(result).not.toBeNull(); + expect(result).toEqual(items[INITIAL_RELEASE_APPK_ITEM_ID]); + expect(result?._source).toHaveProperty("appkChildren"); }); }); diff --git a/lib/libs/api/package/itemExists.ts b/lib/libs/api/package/itemExists.ts index 193077a8b6..694315baa0 100644 --- a/lib/libs/api/package/itemExists.ts +++ b/lib/libs/api/package/itemExists.ts @@ -5,15 +5,10 @@ export async function itemExists(params: { osDomain?: string; indexNamespace?: string; }): Promise { - try { - const packageResult = await os.getItem( - params.osDomain || process.env.osDomain!, - `${params.indexNamespace || process.env.indexNamespace!}main`, - params.id, - ); - return !!packageResult?._source; - } catch (error) { - console.error("Error checking item existence in OpenSearch:", error); - return false; - } + const packageResult = await os.getItem( + params.osDomain || process.env.osDomain!, + `${params.indexNamespace || process.env.indexNamespace!}main`, + params.id, + ); + return !!packageResult?._source; } diff --git a/lib/libs/api/statusMemo.test.ts b/lib/libs/api/statusMemo.test.ts index e0b4ecf1f8..557910215a 100644 --- a/lib/libs/api/statusMemo.test.ts +++ b/lib/libs/api/statusMemo.test.ts @@ -1,10 +1,11 @@ -import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; +import { describe, it, expect, vi, beforeEach, afterAll } from "vitest"; import { buildStatusMemoQuery } from "./statusMemo"; // replace with the actual path to your module describe("buildStatusMemoQuery", () => { const mockDate = new Date("2023-01-01T12:00:00Z").getTime(); - beforeAll(() => { + beforeEach(() => { + vi.useFakeTimers(); vi.setSystemTime(mockDate); }); diff --git a/lib/libs/email/content/email-components.tsx b/lib/libs/email/content/email-components.tsx index 29f2fe8bd4..4884714a8e 100644 --- a/lib/libs/email/content/email-components.tsx +++ b/lib/libs/email/content/email-components.tsx @@ -26,6 +26,8 @@ const areAllAttachmentsEmpty = ( return Object.values(attachments).every((att) => !att || att.files?.length === 0); }; +const Divider = () =>
; + const Textarea = ({ children }: { children: React.ReactNode }) => ( ); -const Divider = () =>
; +const SubDocHowToAccess = ({ + appEndpointURL, +}: { + appEndpointURL: string; + useThisLink?: boolean; +}) => ( + <> + + How to Access: +
    +
  • + + These documents can be found in OneMAC through this link{" "} + {appEndpointURL}. + +
  • +
  • + + If you are not already logged in, click “Login” at the top of the page and log in using + your Enterprise User Administration (EUA) credentials. + +
  • + +
  • + + After you logged in, click the submission ID number on the dashboard page to view details. + +
  • +
+ +); const DetailsHeading = () => (
-
+ Details: @@ -114,7 +146,7 @@ const Attachments = ({ return ( <> -
+ Files: @@ -156,7 +188,7 @@ const PackageDetails = ({ details }: { details: Record }) => if (label === "Summary") { return ( -
+ Summary: @@ -208,7 +240,6 @@ const FollowUpNotice = ({ {includeStateLead ? " or your state lead." : "."} - Thank you. ) : (
@@ -219,27 +250,11 @@ const FollowUpNotice = ({ {includeStateLead ? " or your state lead." : "."} - Thank you.
)} ); -export const SpamWarning = () => ( -
- - - If the contents of this email seem suspicious, do not open them, and instead forward this - email to{" "} - - {EMAIL_CONFIG.SPAM_EMAIL} - - . - - Thank you. -
-); - const EmailFooter = ({ children }: { children: React.ReactNode }) => (
{children}
); @@ -299,6 +314,7 @@ export { Textarea, EmailNav, LoginInstructions, + SubDocHowToAccess, DetailsHeading, Divider, Attachments, diff --git a/lib/libs/email/content/email-templates.tsx b/lib/libs/email/content/email-templates.tsx index 77296f9b45..08ff3848d6 100644 --- a/lib/libs/email/content/email-templates.tsx +++ b/lib/libs/email/content/email-templates.tsx @@ -1,4 +1,4 @@ -import { Body, Container, Head, Heading, Html, Preview } from "@react-email/components"; +import { Body, Container, Head, Heading, Html, Preview, Text } from "@react-email/components"; import { EmailNav, EmailFooter } from "./email-components"; import { styles } from "./email-styles"; import { ReactNode } from "react"; @@ -27,6 +27,7 @@ export const BaseEmailTemplate = ({
{heading} {children} + Thank you.
{footerContent} diff --git a/lib/libs/email/content/new-submission/emailTemplates/AppKCMS.tsx b/lib/libs/email/content/new-submission/emailTemplates/AppKCMS.tsx index a502e61984..0abdffe531 100644 --- a/lib/libs/email/content/new-submission/emailTemplates/AppKCMS.tsx +++ b/lib/libs/email/content/new-submission/emailTemplates/AppKCMS.tsx @@ -4,7 +4,6 @@ import { PackageDetails, Attachments, BasicFooter, - SpamWarning, Divider, } from "../../email-components"; import { BaseEmailTemplate } from "../../email-templates"; @@ -25,7 +24,7 @@ export const AppKCMSEmail = ({ variables }: { variables: AppKEmailProps }) => { { }} /> - ); }; diff --git a/lib/libs/email/content/new-submission/emailTemplates/AppKState.tsx b/lib/libs/email/content/new-submission/emailTemplates/AppKState.tsx index 63285c17b7..2d8d2b44be 100644 --- a/lib/libs/email/content/new-submission/emailTemplates/AppKState.tsx +++ b/lib/libs/email/content/new-submission/emailTemplates/AppKState.tsx @@ -27,7 +27,7 @@ export const AppKStateEmail = (props: { - ); }; diff --git a/lib/libs/email/content/respondToRai/emailTemplates/WaiverState.tsx b/lib/libs/email/content/respondToRai/emailTemplates/WaiverState.tsx index c04a3a4365..b8227af88c 100644 --- a/lib/libs/email/content/respondToRai/emailTemplates/WaiverState.tsx +++ b/lib/libs/email/content/respondToRai/emailTemplates/WaiverState.tsx @@ -26,7 +26,7 @@ export const WaiverStateEmail = (props: { > { + const variables = props.variables; + const previewText = `Action required: review new documents for 1915(c) ${variables.id} in OneMAC.`; + const heading = `New documents have been submitted for 1915(c) ${variables.id} in OneMAC.`; + return ( + } + > + + + + + ); +}; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/AppKState.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/AppKState.tsx new file mode 100644 index 0000000000..cefdac4901 --- /dev/null +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/AppKState.tsx @@ -0,0 +1,38 @@ +import { Events } from "shared-types"; +import { CommonEmailVariables } from "shared-types"; +import { Text } from "@react-email/components"; +import { PackageDetails, Attachments, BasicFooter, Divider } from "../../email-components"; +import { BaseEmailTemplate } from "../../email-templates"; +import { styles } from "../../email-styles"; + +export const AppKStateEmail = (props: { + variables: Events["UploadSubsequentDocuments"] & CommonEmailVariables; +}) => { + const variables = props.variables; + const previewText = `Additional documents submitted for 1915(c) ${variables.id}`; + const heading = `You’ve successfully submitted the following to CMS reviewers for 1915(c) ${variables.id}`; + + return ( + } + > + + + + + If you have questions or did not expect this email, please contact your CPOC. + + + ); +}; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaCMS.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaCMS.tsx index 0be5c1cd58..227dcc1b8c 100644 --- a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaCMS.tsx +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaCMS.tsx @@ -1,13 +1,17 @@ import { CommonEmailVariables, Events } from "shared-types"; -import { PackageDetails, BasicFooter, Attachments } from "../../email-components"; +import { + PackageDetails, + BasicFooter, + Attachments, + SubDocHowToAccess, +} from "../../email-components"; import { BaseEmailTemplate } from "../../email-templates"; -import { styles } from "../../email-styles"; -import { Text } from "@react-email/components"; -export const ChipSpaCMSEmail = (props: { +export const ChipSpaCMSEmail = ({ + variables, +}: { variables: Events["UploadSubsequentDocuments"] & CommonEmailVariables; }) => { - const variables = props.variables; const previewText = `Action required: review new documents for CHIP SPA ${variables.id} in OneMAC.`; const heading = `New documents have been submitted for CHIP SPA ${variables.id} in OneMAC.`; return ( @@ -19,29 +23,13 @@ export const ChipSpaCMSEmail = (props: { > - - How to Access: - - - • These documents can be found in OneMAC through this link{" "} - - {variables.applicationEndpointUrl} - - - - • If you are not already logged in, click “Login” at the top of the page and log in using - your Enterprise User Administration (EUA) credentials. - - - • After you logged in, click the submission ID number on the dashboard page to view details. - - Thank you. + ); }; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaState.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaState.tsx index b202cf2594..d8d0354f5d 100644 --- a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaState.tsx +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaState.tsx @@ -1,14 +1,15 @@ import { Events } from "shared-types"; import { CommonEmailVariables } from "shared-types"; import { Text } from "@react-email/components"; -import { PackageDetails, DetailsHeading, Attachments, BasicFooter } from "../../email-components"; +import { PackageDetails, Attachments, BasicFooter, Divider } from "../../email-components"; import { BaseEmailTemplate } from "../../email-templates"; import { styles } from "../../email-styles"; -export const ChipSpaStateEmail = (props: { +export const ChipSpaStateEmail = ({ + variables, +}: { variables: Events["UploadSubsequentDocuments"] & CommonEmailVariables; }) => { - const variables = props.variables; const previewText = `Additional documents submitted for CHIP SPA ${variables.id}`; const heading = `You’ve successfully submitted the following to CMS reviewers for CHIP SPA ${variables.id}`; @@ -19,10 +20,9 @@ export const ChipSpaStateEmail = (props: { applicationEndpointUrl={variables.applicationEndpointUrl} footerContent={} > - + If you have questions or did not expect this email, please contact your CPOC. - Thank you. ); }; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/MedSpaCMS.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/MedSpaCMS.tsx new file mode 100644 index 0000000000..873c4c9acb --- /dev/null +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/MedSpaCMS.tsx @@ -0,0 +1,33 @@ +import { CommonEmailVariables, Events } from "shared-types"; +import { + PackageDetails, + BasicFooter, + Attachments, + SubDocHowToAccess, +} from "../../email-components"; +import { BaseEmailTemplate } from "../../email-templates"; + +export const MedSpaCMSEmail = (props: { + variables: Events["UploadSubsequentDocuments"] & CommonEmailVariables; +}) => { + const variables = props.variables; + + return ( + } + > + + + + + ); +}; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/MedSpaState.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/MedSpaState.tsx new file mode 100644 index 0000000000..412eea861c --- /dev/null +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/MedSpaState.tsx @@ -0,0 +1,35 @@ +import { Events } from "shared-types"; +import { CommonEmailVariables } from "shared-types"; +import { Text } from "@react-email/components"; +import { PackageDetails, Attachments, BasicFooter } from "../../email-components"; +import { BaseEmailTemplate } from "../../email-templates"; +import { styles } from "../../email-styles"; + +export const MedSpaStateEmail = (props: { + variables: Events["UploadSubsequentDocuments"] & CommonEmailVariables; +}) => { + const variables = props.variables; + + return ( + } + > + + + + If you have questions or did not expect this email, please contact your CPOC. + + + ); +}; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BCMS.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BCMS.tsx new file mode 100644 index 0000000000..fb0fc39e25 --- /dev/null +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BCMS.tsx @@ -0,0 +1,33 @@ +import { CommonEmailVariables, Events } from "shared-types"; +import { + PackageDetails, + BasicFooter, + Attachments, + SubDocHowToAccess, +} from "../../email-components"; +import { BaseEmailTemplate } from "../../email-templates"; + +export const WaiversEmailCMS = ({ + variables, +}: { + variables: Events["UploadSubsequentDocuments"] & CommonEmailVariables; +}) => { + return ( + } + > + + + + + ); +}; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BState.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BState.tsx new file mode 100644 index 0000000000..409b3aade5 --- /dev/null +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BState.tsx @@ -0,0 +1,35 @@ +import { CommonEmailVariables, Events } from "shared-types"; +import { PackageDetails, BasicFooter, Attachments, Divider } from "../../email-components"; +import { styles } from "../../email-styles"; +import { Text } from "@react-email/components"; +import { BaseEmailTemplate } from "../../email-templates"; + +export const WaiversEmailState = ({ + variables, +}: { + variables: Events["UploadSubsequentDocuments"] & CommonEmailVariables; +}) => { + return ( + } + > + + + + + If you have questions or did not expect this email, please contact your CPOC. + + + ); +}; diff --git a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/index.tsx b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/index.tsx index fd031f1134..a28c4435d9 100644 --- a/lib/libs/email/content/upload-subsequent-documents/emailTemplates/index.tsx +++ b/lib/libs/email/content/upload-subsequent-documents/emailTemplates/index.tsx @@ -1,2 +1,8 @@ export { ChipSpaCMSEmail } from "./ChipSpaCMS"; export { ChipSpaStateEmail } from "./ChipSpaState"; +export { MedSpaCMSEmail } from "./MedSpaCMS"; +export { MedSpaStateEmail } from "./MedSpaState"; +export { AppKStateEmail } from "./AppKState"; +export { AppKCMSEmail } from "./AppKCMS"; +export { WaiversEmailCMS } from "./Waiver1915BCMS" +export { WaiversEmailState } from "./Waiver1915BState" diff --git a/lib/libs/email/content/upload-subsequent-documents/index.tsx b/lib/libs/email/content/upload-subsequent-documents/index.tsx index 2ed7f8e1c5..31b99f88bd 100644 --- a/lib/libs/email/content/upload-subsequent-documents/index.tsx +++ b/lib/libs/email/content/upload-subsequent-documents/index.tsx @@ -1,6 +1,6 @@ import { Events, Authority, EmailAddresses, CommonEmailVariables } from "shared-types"; import { AuthoritiesWithUserTypesTemplate } from "../.."; -import { ChipSpaCMSEmail, ChipSpaStateEmail } from "./emailTemplates"; +import { ChipSpaCMSEmail, ChipSpaStateEmail, AppKCMSEmail, AppKStateEmail } from "./emailTemplates"; import { render } from "@react-email/render"; export const uploadSubsequentDocuments: AuthoritiesWithUserTypesTemplate = { @@ -27,4 +27,30 @@ export const uploadSubsequentDocuments: AuthoritiesWithUserTypesTemplate = { }; }, }, + [Authority["1915c"]]: { + cms: async ( + variables: Events["UploadSubsequentDocuments"] & + CommonEmailVariables & { emails: EmailAddresses }, + ) => { + return { + to: [ + ...variables.emails.osgEmail, + ...variables.emails.cpocEmail, + ...variables.emails.srtEmails, + ], + subject: `Action required: review new documents for ${variables.actionType + variables.id}`, + body: await render(), + }; + }, + state: async ( + variables: Events["UploadSubsequentDocuments"] & + CommonEmailVariables & { emails: EmailAddresses }, + ) => { + return { + to: [`${variables.submitterName} <${variables.submitterEmail}>`], + subject: `Additional documents submitted for ${variables.actionType + variables.id}`, + body: await render(), + }; + }, + }, }; diff --git a/lib/libs/email/content/vitest.config.ts b/lib/libs/email/content/vitest.config.ts new file mode 100644 index 0000000000..980c24e897 --- /dev/null +++ b/lib/libs/email/content/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineProject } from "vitest/config"; + +export default defineProject({ + test: { + name: "email", + root: ".", + setupFiles: ["./vitest.setup.ts"], + exclude: ["**/node_modules/**"], + environment: "jsdom", + }, +}); diff --git a/lib/libs/email/content/vitest.setup.ts b/lib/libs/email/content/vitest.setup.ts new file mode 100644 index 0000000000..15a05336f9 --- /dev/null +++ b/lib/libs/email/content/vitest.setup.ts @@ -0,0 +1,28 @@ +import { mockedServer } from "mocks/server"; +import { afterAll, afterEach, beforeAll, vi } from "vitest"; + + +beforeAll(() => { + vi.spyOn(console, "error").mockImplementation(() => {}); + + console.log("starting MSW listener for email tests"); + mockedServer.listen({ + onUnhandledRequest: "warn", + }); +}); + +afterEach(() => { + vi.useRealTimers(); + vi.clearAllMocks(); + + // Reset any request handlers that we may add during the tests, + // so they don't affect other tests. + mockedServer.resetHandlers(); +}); + +afterAll(() => { + vi.clearAllMocks(); + + // Clean up after the tests are finished. + mockedServer.close(); +}); diff --git a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx index fdc3406ef5..af88ecdedd 100644 --- a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx +++ b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx @@ -1,5 +1,5 @@ import { CommonEmailVariables, Events } from "lib/packages/shared-types"; -import { BasicFooter, Divider, PackageDetails, SpamWarning } from "../../email-components"; +import { BasicFooter, Divider, PackageDetails } from "../../email-components"; import { BaseEmailTemplate } from "../../email-templates"; export const ChipSpaCMSEmail = ({ @@ -16,13 +16,12 @@ export const ChipSpaCMSEmail = ({ - ); diff --git a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx index e3fc23ff33..e7e3ed7110 100644 --- a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx +++ b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx @@ -16,7 +16,7 @@ export const ChipSpaStateEmail = ({ - ); diff --git a/lib/libs/email/content/withdrawPackage/emailTemplates/MedSpaState.tsx b/lib/libs/email/content/withdrawPackage/emailTemplates/MedSpaState.tsx index 0a18e4dd97..ff107135b9 100644 --- a/lib/libs/email/content/withdrawPackage/emailTemplates/MedSpaState.tsx +++ b/lib/libs/email/content/withdrawPackage/emailTemplates/MedSpaState.tsx @@ -16,7 +16,7 @@ export const MedSpaStateEmail = ({ { - const previewText = `Withdrawal of ${variables.authority} ${variables.id}`; - const heading = - "The OneMAC Submission Portal received a request to withdraw the package below. The package will no longer be considered for CMS review:"; - return ( - } - > - - - - ); -}; +}) => ( + } + > + + +); diff --git a/lib/libs/email/content/withdrawPackage/emailTemplates/WaiverState.tsx b/lib/libs/email/content/withdrawPackage/emailTemplates/WaiverState.tsx index 05bf261a4f..85f7b3d5d7 100644 --- a/lib/libs/email/content/withdrawPackage/emailTemplates/WaiverState.tsx +++ b/lib/libs/email/content/withdrawPackage/emailTemplates/WaiverState.tsx @@ -15,14 +15,16 @@ export const WaiverStateEmail = ({ }) => { return ( } > - ); }; diff --git a/lib/libs/email/content/withdrawRai/emailTemplates/AppKState.tsx b/lib/libs/email/content/withdrawRai/emailTemplates/AppKState.tsx index 9ea0d17c1c..56ffb5ac98 100644 --- a/lib/libs/email/content/withdrawRai/emailTemplates/AppKState.tsx +++ b/lib/libs/email/content/withdrawRai/emailTemplates/AppKState.tsx @@ -22,7 +22,7 @@ export const AppKStateEmail = (props: { > ( // I think this needs to be written to handle not finding any matching events and so forth export async function getLatestMatchingEvent(id: string, actionType: string) { - const item = await getPackageChangelog(id); - const events = item.hits.hits.filter((hit: any) => hit._source.actionType === actionType); - events.sort((a: any, b: any) => b._source.timestamp - a._source.timestamp); - const latestMatchingEvent = events[0]._source; - return latestMatchingEvent; + try { + const item = await getPackageChangelog(id); + const events = item.hits.hits.filter((hit: any) => hit._source.actionType === actionType); + events.sort((a: any, b: any) => b._source.timestamp - a._source.timestamp); + const latestMatchingEvent = events[0]._source; + return latestMatchingEvent; + } catch (error) { + console.error({ error }) + return null; + } } export * from "./getAllStateUsers"; diff --git a/lib/libs/email/mock-data/upload-subsequent-documents.ts b/lib/libs/email/mock-data/upload-subsequent-documents.ts index 4548997448..6a66e8ea7c 100644 --- a/lib/libs/email/mock-data/upload-subsequent-documents.ts +++ b/lib/libs/email/mock-data/upload-subsequent-documents.ts @@ -32,7 +32,7 @@ export const emailTemplateValue = { }, }, additionalInformation: - "This some additional infornormaiton about the request to withdraw and what makes it important.", + "This some additional information about the request to upload additional documents.", submitterName: "George Harrison", submitterEmail: "george@example.com", timestamp: 1723390633663, diff --git a/lib/libs/email/preview/Upload Subsequent Documents/CMS/AppK.tsx b/lib/libs/email/preview/Upload Subsequent Documents/CMS/AppK.tsx new file mode 100644 index 0000000000..b18abb99bb --- /dev/null +++ b/lib/libs/email/preview/Upload Subsequent Documents/CMS/AppK.tsx @@ -0,0 +1,24 @@ +import { AppKCMSEmail } from "../../../content/upload-subsequent-documents/emailTemplates"; +import { emailTemplateValue } from "../../../mock-data/new-submission"; +import * as attachments from "../../../mock-data/attachments"; +const AppKCMSEmailPreview = () => { + return ( + + ); +}; + +export default AppKCMSEmailPreview; diff --git a/lib/libs/email/preview/Upload Subsequent Documents/CMS/CHIP_SPA.tsx b/lib/libs/email/preview/Upload Subsequent Documents/CMS/CHIP_SPA.tsx index 0ee8ce9d25..ceec9f1c25 100644 --- a/lib/libs/email/preview/Upload Subsequent Documents/CMS/CHIP_SPA.tsx +++ b/lib/libs/email/preview/Upload Subsequent Documents/CMS/CHIP_SPA.tsx @@ -1,4 +1,4 @@ -import { ChipSpaCMSEmail } from "lib/libs/email/content/upload-subsequent-documents/emailTemplates/ChipSpaCMS"; +import { ChipSpaCMSEmail } from "lib/libs/email/content/upload-subsequent-documents/emailTemplates"; import { emailTemplateValue } from "lib/libs/email/mock-data/upload-subsequent-documents"; import * as attachments from "lib/libs/email/mock-data/attachments"; @@ -10,6 +10,7 @@ const ChipSpaCMSEmailPreview = () => { id: "CO-24-1234", event: "upload-subsequent-documents", actionType: "Amend", + authority: "CHIP SPA", attachments: { currentStatePlan: attachments.currentStatePlan, amendedLanguage: attachments.amendedLanguage, diff --git a/lib/libs/email/preview/Upload Subsequent Documents/CMS/MED_SPA.tsx b/lib/libs/email/preview/Upload Subsequent Documents/CMS/MED_SPA.tsx new file mode 100644 index 0000000000..7b41cf0a4d --- /dev/null +++ b/lib/libs/email/preview/Upload Subsequent Documents/CMS/MED_SPA.tsx @@ -0,0 +1,28 @@ +import { MedSpaCMSEmail } from "lib/libs/email/content/upload-subsequent-documents/emailTemplates"; +import { emailTemplateValue } from "lib/libs/email/mock-data/upload-subsequent-documents"; +import * as attachments from "lib/libs/email/mock-data/attachments"; + +const ChipSpaCMSEmailPreview = () => { + return ( + + ); +}; + +export default ChipSpaCMSEmailPreview; diff --git a/lib/libs/email/preview/Upload Subsequent Documents/CMS/Waiver1915b.tsx b/lib/libs/email/preview/Upload Subsequent Documents/CMS/Waiver1915b.tsx new file mode 100644 index 0000000000..586e0093d5 --- /dev/null +++ b/lib/libs/email/preview/Upload Subsequent Documents/CMS/Waiver1915b.tsx @@ -0,0 +1,28 @@ +import { WaiversEmailCMS } from "lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BCMS"; +import { emailTemplateValue } from "lib/libs/email/mock-data/upload-subsequent-documents"; +import * as attachments from "lib/libs/email/mock-data/attachments"; + +const Waiver1915bCMSEmail = () => { + return ( + + ); +}; + +export default Waiver1915bCMSEmail; diff --git a/lib/libs/email/preview/Upload Subsequent Documents/State/AppK.tsx b/lib/libs/email/preview/Upload Subsequent Documents/State/AppK.tsx new file mode 100644 index 0000000000..18d62c7d83 --- /dev/null +++ b/lib/libs/email/preview/Upload Subsequent Documents/State/AppK.tsx @@ -0,0 +1,24 @@ +import { AppKStateEmail } from "../../../content/upload-subsequent-documents/emailTemplates"; +import { emailTemplateValue } from "../../../mock-data/new-submission"; +import * as attachments from "../../../mock-data/attachments"; +const AppKCMSEmailPreview = () => { + return ( + + ); +}; + +export default AppKCMSEmailPreview; diff --git a/lib/libs/email/preview/Upload Subsequent Documents/State/CHIP_SPA.tsx b/lib/libs/email/preview/Upload Subsequent Documents/State/CHIP_SPA.tsx index 5c1b7e9471..053adeb217 100644 --- a/lib/libs/email/preview/Upload Subsequent Documents/State/CHIP_SPA.tsx +++ b/lib/libs/email/preview/Upload Subsequent Documents/State/CHIP_SPA.tsx @@ -9,6 +9,7 @@ const ChipSpaStateEmailPreview = () => { id: "CO-24-1234", event: "upload-subsequent-documents", actionType: "Amend", + authority: "CHIP SPA", attachments: { currentStatePlan: attachments.currentStatePlan, amendedLanguage: attachments.amendedLanguage, diff --git a/lib/libs/email/preview/Upload Subsequent Documents/State/MED_SPA.tsx b/lib/libs/email/preview/Upload Subsequent Documents/State/MED_SPA.tsx new file mode 100644 index 0000000000..44e2ac684c --- /dev/null +++ b/lib/libs/email/preview/Upload Subsequent Documents/State/MED_SPA.tsx @@ -0,0 +1,27 @@ +import { emailTemplateValue } from "lib/libs/email/mock-data/upload-subsequent-documents"; +import { MedSpaStateEmail } from "lib/libs/email/content/upload-subsequent-documents/emailTemplates"; +import * as attachments from "lib/libs/email/mock-data/attachments"; +const ChipSpaStateEmailPreview = () => { + return ( + + ); +}; + +export default ChipSpaStateEmailPreview; diff --git a/lib/libs/email/preview/Upload Subsequent Documents/State/Waiver1915b.tsx b/lib/libs/email/preview/Upload Subsequent Documents/State/Waiver1915b.tsx new file mode 100644 index 0000000000..675eb3e258 --- /dev/null +++ b/lib/libs/email/preview/Upload Subsequent Documents/State/Waiver1915b.tsx @@ -0,0 +1,28 @@ +import { WaiversEmailState } from "lib/libs/email/content/upload-subsequent-documents/emailTemplates/Waiver1915BState"; +import { emailTemplateValue } from "lib/libs/email/mock-data/upload-subsequent-documents"; +import * as attachments from "lib/libs/email/mock-data/attachments"; + +const Waiver1915bCMSEmail = () => { + return ( + + ); +}; + +export default Waiver1915bCMSEmail; diff --git a/lib/libs/email/preview/WithdrawConfirmation/State/Waiver.tsx b/lib/libs/email/preview/WithdrawConfirmation/State/Waiver.tsx new file mode 100644 index 0000000000..8e2a17ede8 --- /dev/null +++ b/lib/libs/email/preview/WithdrawConfirmation/State/Waiver.tsx @@ -0,0 +1,22 @@ +import { WaiverStateEmail } from "lib/libs/email/content/withdrawConfirmation/emailTemplates"; +import { emailTemplateValue } from "lib/libs/email/mock-data/new-submission"; +import * as attachments from "../../../mock-data/attachments"; + +export default () => { + return ( + + ); +}; diff --git a/lib/libs/index.ts b/lib/libs/index.ts index 3a3ab191b7..0d4176390f 100644 --- a/lib/libs/index.ts +++ b/lib/libs/index.ts @@ -1,2 +1,3 @@ export * from "./opensearch-lib"; export * from "./env"; +export * from "./sink-lib"; diff --git a/lib/libs/opensearch-lib.ts b/lib/libs/opensearch-lib.ts index 830693ce96..25ef6020c6 100644 --- a/lib/libs/opensearch-lib.ts +++ b/lib/libs/opensearch-lib.ts @@ -1,12 +1,12 @@ -import { Client, Connection } from "@opensearch-project/opensearch"; +import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; import { defaultProvider } from "@aws-sdk/credential-provider-node"; +import { Client, Connection, errors as OpensearchErrors } from "@opensearch-project/opensearch"; import * as aws4 from "aws4"; -import axios from "axios"; import { aws4Interceptor } from "aws4-axios"; -import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts"; -import { opensearch } from "shared-types"; -import { errors as OpensearchErrors } from "@opensearch-project/opensearch"; +import axios from "axios"; import { ItemResult, Document as OSDocument } from "lib/packages/shared-types/opensearch/main"; +import { opensearch } from "shared-types"; +import { getDomainAndNamespace } from "./sink-lib"; let client: Client; @@ -168,15 +168,11 @@ export async function mapRole( export async function search(host: string, index: opensearch.Index, query: any) { client = client || (await getClient(host)); - try { - const response = await client.search({ - index: index, - body: query, - }); - return decodeUtf8(response).body; - } catch (e) { - console.log({ e }); - } + const response = await client.search({ + index: index, + body: query, + }); + return decodeUtf8(response).body; } export async function getItem( @@ -184,25 +180,24 @@ export async function getItem( index: opensearch.Index, id: string, ): Promise { - client = client || (await getClient(host)); try { + client = client || (await getClient(host)); const response = await client.get({ id, index }); return decodeUtf8(response).body; - } catch (e) { - console.log({ e }); - return undefined; + } catch (error) { + if (error instanceof OpensearchErrors.ResponseError && error.statusCode === 404 || error.meta?.statusCode === 404) { + console.log("Error (404) retrieving in OpenSearch:", error); + return undefined + } + throw error; } } -export async function getItems( - host: string, - indexNamespace: string, - ids: string[], -): Promise { +export async function getItems(ids: string[]): Promise { try { - const index = `${indexNamespace}main`; + const { domain, index } = getDomainAndNamespace("main"); - client = client || (await getClient(host)); + client = client || (await getClient(domain)); const response = await client.mget<{ docs: ItemResult[] }>({ index, diff --git a/lib/libs/sink-lib.test.ts b/lib/libs/sink-lib.test.ts new file mode 100644 index 0000000000..d3723739b8 --- /dev/null +++ b/lib/libs/sink-lib.test.ts @@ -0,0 +1,37 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import * as os from "./opensearch-lib"; +import { bulkUpdateDataWrapper } from "./sink-lib"; +import { OPENSEARCH_DOMAIN, OPENSEARCH_INDEX_NAMESPACE } from "mocks"; + +describe("bulkUpdateDataWrapper", () => { + const DOCS = [{ id: "1" }]; + + afterEach(() => { + vi.restoreAllMocks(); + vi.resetModules(); + }); + + it("calls bulkUpdateData with correct arguments when env vars are defined", async () => { + const mockBulkUpdateData = vi.spyOn(os, "bulkUpdateData").mockImplementation(vi.fn()); + + await bulkUpdateDataWrapper(DOCS, "main"); + + expect(mockBulkUpdateData).toHaveBeenCalledWith(OPENSEARCH_DOMAIN, `${OPENSEARCH_INDEX_NAMESPACE}main`, DOCS); + }); + + it("throws an Error when env vars are missing", async () => { + delete process.env.osDomain; + delete process.env.indexNamespace; + + await expect(bulkUpdateDataWrapper(DOCS, "main")).rejects.toThrow( + "osDomain is undefined in environment variables", + ); + + vi.stubEnv("osDomain", "os-domain"); + process.env.osDomain = OPENSEARCH_DOMAIN; + + await expect(bulkUpdateDataWrapper(DOCS, "main")).rejects.toThrow( + "indexName is undefined in environment variables", + ); + }); +}); diff --git a/lib/libs/sink-lib.ts b/lib/libs/sink-lib.ts index a0abb9ac09..d5ac0a2853 100644 --- a/lib/libs/sink-lib.ts +++ b/lib/libs/sink-lib.ts @@ -2,6 +2,7 @@ import pino from "pino"; const logger = pino(); import * as os from "./opensearch-lib"; +import { BaseIndex } from "lib/packages/shared-types/opensearch"; export function getTopic(topicPartition: string) { return topicPartition.split("--").pop()?.split("-").slice(0, -1)[0]; @@ -19,8 +20,7 @@ const ErrorMessages = { [ErrorType.VALIDATION]: "A validation error occurred.", [ErrorType.UNKNOWN]: "An unknown error occurred.", [ErrorType.BULKUPDATE]: "An error occurred while bulk updating records.", - [ErrorType.BADTOPIC]: - "Topic is unknown, unsupported, or unable to be parsed.", + [ErrorType.BADTOPIC]: "Topic is unknown, unsupported, or unable to be parsed.", [ErrorType.BADPARSE]: "An error occurred while parsing the record.", }; @@ -82,17 +82,45 @@ const prettyPrintJsonInObject = (obj: any): any => { return obj; }; +/** + * Returns the `osDomain` and `indexNamespace` env variables. Passing `baseIndex` appends the arg to the `index` variable + * @throws if env variables are not defined, `getDomainAndNamespace` throws error indicating which variable is missing + * @returns + */ +export function getDomainAndNamespace( + baseIndex: T, +): { domain: string; index: `${string}${T}` }; +export function getDomainAndNamespace(): { domain: string; index: string }; +export function getDomainAndNamespace(baseIndex?: BaseIndex) { + const domain = process.env.osDomain; + + if (domain === undefined) { + throw new Error("osDomain is undefined in environment variables"); + } + + const indexNamespace = process.env.indexNamespace; + + if (indexNamespace === undefined) { + throw new Error("indexName is undefined in environment variables"); + } + + return { index: baseIndex ? `${indexNamespace}${baseIndex}` : indexNamespace, domain }; +} + export async function bulkUpdateDataWrapper( - domain: string, - index: string, - docs: any[], + docs: { id: string; [key: string]: unknown }[], + baseIndex: BaseIndex, ) { try { - await os.bulkUpdateData(process.env.osDomain!, index, docs); - } catch (error: any) { + const { domain, index } = getDomainAndNamespace(baseIndex); + + await os.bulkUpdateData(domain, index, docs); + } catch (error) { logError({ type: ErrorType.BULKUPDATE, + error, }); + throw error; } } diff --git a/lib/local-constructs/clamav-scanning/package-lock.json b/lib/local-constructs/clamav-scanning/package-lock.json index e35a65edc1..5f3aed2d13 100644 --- a/lib/local-constructs/clamav-scanning/package-lock.json +++ b/lib/local-constructs/clamav-scanning/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@aws-sdk/client-s3": "^3.688.0", + "@aws-sdk/client-s3": "^3.697.0", "@types/mime-types": "^2.1.4", "file-type": "^19.6.0", "mime-types": "^2.1.35", @@ -214,68 +214,68 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.688.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.688.0.tgz", - "integrity": "sha512-bLyF7gT0RTWrsJPxbaslg1xP1gUdw3BJVvgfWM/63BDBpVCqIk9YlrXfJwjImcKguxGp8sCTdttywmfdPwQEfg==", + "version": "3.697.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.697.0.tgz", + "integrity": "sha512-NJ5V9whlb99YVAG/b0nLSb9sM/ZrEJZC+JT3Skxu7dgo6LIFLavdXjEtkR+rFdG34Gps34jwJ7wvKG2mp1zKHg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.687.0", - "@aws-sdk/client-sts": "3.687.0", - "@aws-sdk/core": "3.686.0", - "@aws-sdk/credential-provider-node": "3.687.0", - "@aws-sdk/middleware-bucket-endpoint": "3.686.0", - "@aws-sdk/middleware-expect-continue": "3.686.0", - "@aws-sdk/middleware-flexible-checksums": "3.688.0", - "@aws-sdk/middleware-host-header": "3.686.0", - "@aws-sdk/middleware-location-constraint": "3.686.0", - "@aws-sdk/middleware-logger": "3.686.0", - "@aws-sdk/middleware-recursion-detection": "3.686.0", - "@aws-sdk/middleware-sdk-s3": "3.687.0", - "@aws-sdk/middleware-ssec": "3.686.0", - "@aws-sdk/middleware-user-agent": "3.687.0", - "@aws-sdk/region-config-resolver": "3.686.0", - "@aws-sdk/signature-v4-multi-region": "3.687.0", - "@aws-sdk/types": "3.686.0", - "@aws-sdk/util-endpoints": "3.686.0", - "@aws-sdk/util-user-agent-browser": "3.686.0", - "@aws-sdk/util-user-agent-node": "3.687.0", - "@aws-sdk/xml-builder": "3.686.0", - "@smithy/config-resolver": "^3.0.10", - "@smithy/core": "^2.5.1", - "@smithy/eventstream-serde-browser": "^3.0.11", - "@smithy/eventstream-serde-config-resolver": "^3.0.8", - "@smithy/eventstream-serde-node": "^3.0.10", - "@smithy/fetch-http-handler": "^4.0.0", - "@smithy/hash-blob-browser": "^3.1.7", - "@smithy/hash-node": "^3.0.8", - "@smithy/hash-stream-node": "^3.1.7", - "@smithy/invalid-dependency": "^3.0.8", - "@smithy/md5-js": "^3.0.8", - "@smithy/middleware-content-length": "^3.0.10", - "@smithy/middleware-endpoint": "^3.2.1", - "@smithy/middleware-retry": "^3.0.25", - "@smithy/middleware-serde": "^3.0.8", - "@smithy/middleware-stack": "^3.0.8", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/node-http-handler": "^3.2.5", - "@smithy/protocol-http": "^4.1.5", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", - "@smithy/url-parser": "^3.0.8", + "@aws-sdk/client-sso-oidc": "3.696.0", + "@aws-sdk/client-sts": "3.696.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.696.0", + "@aws-sdk/middleware-bucket-endpoint": "3.696.0", + "@aws-sdk/middleware-expect-continue": "3.696.0", + "@aws-sdk/middleware-flexible-checksums": "3.697.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-location-constraint": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-sdk-s3": "3.696.0", + "@aws-sdk/middleware-ssec": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/signature-v4-multi-region": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@aws-sdk/xml-builder": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/eventstream-serde-browser": "^3.0.13", + "@smithy/eventstream-serde-config-resolver": "^3.0.10", + "@smithy/eventstream-serde-node": "^3.0.12", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-blob-browser": "^3.1.9", + "@smithy/hash-node": "^3.0.10", + "@smithy/hash-stream-node": "^3.1.9", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/md5-js": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.25", - "@smithy/util-defaults-mode-node": "^3.0.25", - "@smithy/util-endpoints": "^2.1.4", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-retry": "^3.0.8", - "@smithy/util-stream": "^3.2.1", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", + "@smithy/util-stream": "^3.3.1", "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.7", + "@smithy/util-waiter": "^3.1.9", "tslib": "^2.6.2" }, "engines": { @@ -283,47 +283,47 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.687.0.tgz", - "integrity": "sha512-dfj0y9fQyX4kFill/ZG0BqBTLQILKlL7+O5M4F9xlsh2WNuV2St6WtcOg14Y1j5UODPJiJs//pO+mD1lihT5Kw==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.696.0.tgz", + "integrity": "sha512-q5TTkd08JS0DOkHfUL853tuArf7NrPeqoS5UOvqJho8ibV9Ak/a/HO4kNvy9Nj3cib/toHYHsQIEtecUPSUUrQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.686.0", - "@aws-sdk/middleware-host-header": "3.686.0", - "@aws-sdk/middleware-logger": "3.686.0", - "@aws-sdk/middleware-recursion-detection": "3.686.0", - "@aws-sdk/middleware-user-agent": "3.687.0", - "@aws-sdk/region-config-resolver": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@aws-sdk/util-endpoints": "3.686.0", - "@aws-sdk/util-user-agent-browser": "3.686.0", - "@aws-sdk/util-user-agent-node": "3.687.0", - "@smithy/config-resolver": "^3.0.10", - "@smithy/core": "^2.5.1", - "@smithy/fetch-http-handler": "^4.0.0", - "@smithy/hash-node": "^3.0.8", - "@smithy/invalid-dependency": "^3.0.8", - "@smithy/middleware-content-length": "^3.0.10", - "@smithy/middleware-endpoint": "^3.2.1", - "@smithy/middleware-retry": "^3.0.25", - "@smithy/middleware-serde": "^3.0.8", - "@smithy/middleware-stack": "^3.0.8", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/node-http-handler": "^3.2.5", - "@smithy/protocol-http": "^4.1.5", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", - "@smithy/url-parser": "^3.0.8", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.25", - "@smithy/util-defaults-mode-node": "^3.0.25", - "@smithy/util-endpoints": "^2.1.4", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-retry": "^3.0.8", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -332,48 +332,48 @@ } }, "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.687.0.tgz", - "integrity": "sha512-Rdd8kLeTeh+L5ZuG4WQnWgYgdv7NorytKdZsGjiag1D8Wv3PcJvPqqWdgnI0Og717BSXVoaTYaN34FyqFYSx6Q==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.696.0.tgz", + "integrity": "sha512-ikxQ3mo86d1mAq5zTaQAh8rLBERwL+I4MUYu/IVYW2hhl9J2SDsl0SgnKeXQG6S8zWuHcBO587zsZaRta1MQ/g==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.686.0", - "@aws-sdk/credential-provider-node": "3.687.0", - "@aws-sdk/middleware-host-header": "3.686.0", - "@aws-sdk/middleware-logger": "3.686.0", - "@aws-sdk/middleware-recursion-detection": "3.686.0", - "@aws-sdk/middleware-user-agent": "3.687.0", - "@aws-sdk/region-config-resolver": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@aws-sdk/util-endpoints": "3.686.0", - "@aws-sdk/util-user-agent-browser": "3.686.0", - "@aws-sdk/util-user-agent-node": "3.687.0", - "@smithy/config-resolver": "^3.0.10", - "@smithy/core": "^2.5.1", - "@smithy/fetch-http-handler": "^4.0.0", - "@smithy/hash-node": "^3.0.8", - "@smithy/invalid-dependency": "^3.0.8", - "@smithy/middleware-content-length": "^3.0.10", - "@smithy/middleware-endpoint": "^3.2.1", - "@smithy/middleware-retry": "^3.0.25", - "@smithy/middleware-serde": "^3.0.8", - "@smithy/middleware-stack": "^3.0.8", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/node-http-handler": "^3.2.5", - "@smithy/protocol-http": "^4.1.5", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", - "@smithy/url-parser": "^3.0.8", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.696.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.25", - "@smithy/util-defaults-mode-node": "^3.0.25", - "@smithy/util-endpoints": "^2.1.4", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-retry": "^3.0.8", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -381,53 +381,53 @@ "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.687.0" + "@aws-sdk/client-sts": "^3.696.0" } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.687.0.tgz", - "integrity": "sha512-SQjDH8O4XCTtouuCVYggB0cCCrIaTzUZIkgJUpOsIEJBLlTbNOb/BZqUShAQw2o9vxr2rCeOGjAQOYPysW/Pmg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.696.0.tgz", + "integrity": "sha512-eJOxR8/UyI7kGSRyE751Ea7MKEzllQs7eNveDJy9OP4t/jsN/P19HJ1YHeA1np40JRTUBfqa6WLAAiIXsk8rkg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.687.0", - "@aws-sdk/core": "3.686.0", - "@aws-sdk/credential-provider-node": "3.687.0", - "@aws-sdk/middleware-host-header": "3.686.0", - "@aws-sdk/middleware-logger": "3.686.0", - "@aws-sdk/middleware-recursion-detection": "3.686.0", - "@aws-sdk/middleware-user-agent": "3.687.0", - "@aws-sdk/region-config-resolver": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@aws-sdk/util-endpoints": "3.686.0", - "@aws-sdk/util-user-agent-browser": "3.686.0", - "@aws-sdk/util-user-agent-node": "3.687.0", - "@smithy/config-resolver": "^3.0.10", - "@smithy/core": "^2.5.1", - "@smithy/fetch-http-handler": "^4.0.0", - "@smithy/hash-node": "^3.0.8", - "@smithy/invalid-dependency": "^3.0.8", - "@smithy/middleware-content-length": "^3.0.10", - "@smithy/middleware-endpoint": "^3.2.1", - "@smithy/middleware-retry": "^3.0.25", - "@smithy/middleware-serde": "^3.0.8", - "@smithy/middleware-stack": "^3.0.8", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/node-http-handler": "^3.2.5", - "@smithy/protocol-http": "^4.1.5", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", - "@smithy/url-parser": "^3.0.8", + "@aws-sdk/client-sso-oidc": "3.696.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.696.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.25", - "@smithy/util-defaults-mode-node": "^3.0.25", - "@smithy/util-endpoints": "^2.1.4", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-retry": "^3.0.8", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -436,20 +436,20 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.686.0.tgz", - "integrity": "sha512-Xt3DV4DnAT3v2WURwzTxWQK34Ew+iiLzoUoguvLaZrVMFOqMMrwVjP+sizqIaHp1j7rGmFcN5I8saXnsDLuQLA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/core": "^2.5.1", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/property-provider": "^3.1.7", - "@smithy/protocol-http": "^4.1.5", - "@smithy/signature-v4": "^4.2.0", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", - "@smithy/util-middleware": "^3.0.8", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.696.0.tgz", + "integrity": "sha512-3c9III1k03DgvRZWg8vhVmfIXPG6hAciN9MzQTzqGngzWAELZF/WONRTRQuDFixVtarQatmLHYVw/atGeA2Byw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/core": "^2.5.3", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.7", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-middleware": "^3.0.10", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, @@ -458,15 +458,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.686.0.tgz", - "integrity": "sha512-osD7lPO8OREkgxPiTWmA1i6XEmOth1uW9HWWj/+A2YGCj1G/t2sHu931w4Qj9NWHYZtbTTXQYVRg+TErALV7nQ==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.696.0.tgz", + "integrity": "sha512-T9iMFnJL7YTlESLpVFT3fg1Lkb1lD+oiaIC8KMpepb01gDUBIpj9+Y+pA/cgRWW0yRxmkDXNazAE2qQTVFGJzA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/types": "^3.6.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -474,20 +474,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.686.0.tgz", - "integrity": "sha512-xyGAD/f3vR/wssUiZrNFWQWXZvI4zRm2wpHhoHA1cC2fbRMNFYtFn365yw6dU7l00ZLcdFB1H119AYIUZS7xbw==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.696.0.tgz", + "integrity": "sha512-GV6EbvPi2eq1+WgY/o2RFA3P7HGmnkIzCNmhwtALFlqMroLYWKE7PSeHw66Uh1dFQeVESn0/+hiUNhu1mB0emA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@smithy/fetch-http-handler": "^4.0.0", - "@smithy/node-http-handler": "^3.2.5", - "@smithy/property-provider": "^3.1.7", - "@smithy/protocol-http": "^4.1.5", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", - "@smithy/util-stream": "^3.2.1", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-stream": "^3.3.1", "tslib": "^2.6.2" }, "engines": { @@ -495,48 +495,48 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.687.0.tgz", - "integrity": "sha512-6d5ZJeZch+ZosJccksN0PuXv7OSnYEmanGCnbhUqmUSz9uaVX6knZZfHCZJRgNcfSqg9QC0zsFA/51W5HCUqSQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.686.0", - "@aws-sdk/credential-provider-env": "3.686.0", - "@aws-sdk/credential-provider-http": "3.686.0", - "@aws-sdk/credential-provider-process": "3.686.0", - "@aws-sdk/credential-provider-sso": "3.687.0", - "@aws-sdk/credential-provider-web-identity": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@smithy/credential-provider-imds": "^3.2.4", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.6.0", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.696.0.tgz", + "integrity": "sha512-9WsZZofjPjNAAZhIh7c7FOhLK8CR3RnGgUm1tdZzV6ZSM1BuS2m6rdwIilRxAh3fxxKDkmW/r/aYmmCYwA+AYA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-env": "3.696.0", + "@aws-sdk/credential-provider-http": "3.696.0", + "@aws-sdk/credential-provider-process": "3.696.0", + "@aws-sdk/credential-provider-sso": "3.696.0", + "@aws-sdk/credential-provider-web-identity": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.687.0" + "@aws-sdk/client-sts": "^3.696.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.687.0.tgz", - "integrity": "sha512-Pqld8Nx11NYaBUrVk3bYiGGpLCxkz8iTONlpQWoVWFhSOzlO7zloNOaYbD2XgFjjqhjlKzE91drs/f41uGeCTA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.686.0", - "@aws-sdk/credential-provider-http": "3.686.0", - "@aws-sdk/credential-provider-ini": "3.687.0", - "@aws-sdk/credential-provider-process": "3.686.0", - "@aws-sdk/credential-provider-sso": "3.687.0", - "@aws-sdk/credential-provider-web-identity": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@smithy/credential-provider-imds": "^3.2.4", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.6.0", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.696.0.tgz", + "integrity": "sha512-8F6y5FcfRuMJouC5s207Ko1mcVvOXReBOlJmhIwE4QH1CnO/CliIyepnAZrRQ659mo5wIuquz6gXnpYbitEVMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.696.0", + "@aws-sdk/credential-provider-http": "3.696.0", + "@aws-sdk/credential-provider-ini": "3.696.0", + "@aws-sdk/credential-provider-process": "3.696.0", + "@aws-sdk/credential-provider-sso": "3.696.0", + "@aws-sdk/credential-provider-web-identity": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -544,16 +544,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.686.0.tgz", - "integrity": "sha512-sXqaAgyzMOc+dm4CnzAR5Q6S9OWVHyZjLfW6IQkmGjqeQXmZl24c4E82+w64C+CTkJrFLzH1VNOYp1Hy5gE6Qw==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.696.0.tgz", + "integrity": "sha512-mL1RcFDe9sfmyU5K1nuFkO8UiJXXxLX4JO1gVaDIOvPqwStpUAwi3A1BoeZhWZZNQsiKI810RnYGo0E0WB/hUA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.6.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -561,18 +561,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.687.0.tgz", - "integrity": "sha512-N1YCoE7DovIRF2ReyRrA4PZzF0WNi4ObPwdQQkVxhvSm7PwjbWxrfq7rpYB+6YB1Uq3QPzgVwUFONE36rdpxUQ==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.696.0.tgz", + "integrity": "sha512-4SSZ9Nk08JSu4/rX1a+dEac/Ims1HCXfV7YLUe5LGdtRLSKRoQQUy+hkFaGYoSugP/p1UfUPl3BuTO9Vv8z1pA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.687.0", - "@aws-sdk/core": "3.686.0", - "@aws-sdk/token-providers": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.6.0", + "@aws-sdk/client-sso": "3.696.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/token-providers": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -580,35 +580,35 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.686.0.tgz", - "integrity": "sha512-40UqCpPxyHCXDP7CGd9JIOZDgDZf+u1OyLaGBpjQJlz1HYuEsIWnnbTe29Yg3Ah/Zc3g4NBWcUdlGVotlnpnDg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.696.0.tgz", + "integrity": "sha512-XJ/CVlWChM0VCoc259vWguFUjJDn/QwDqHwbx+K9cg3v6yrqXfK5ai+p/6lx0nQpnk4JzPVeYYxWRpaTsGC9rg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/types": "^3.6.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sts": "^3.686.0" + "@aws-sdk/client-sts": "^3.696.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.686.0.tgz", - "integrity": "sha512-6qCoWI73/HDzQE745MHQUYz46cAQxHCgy1You8MZQX9vHAQwqBnkcsb2hGp7S6fnQY5bNsiZkMWVQ/LVd2MNjg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.696.0.tgz", + "integrity": "sha512-V07jishKHUS5heRNGFpCWCSTjRJyQLynS/ncUeE8ZYtG66StOOQWftTwDfFOSoXlIqrXgb4oT9atryzXq7Z4LQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@aws-sdk/util-arn-parser": "3.679.0", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "@smithy/util-config-provider": "^3.0.0", "tslib": "^2.6.2" }, @@ -617,14 +617,14 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.686.0.tgz", - "integrity": "sha512-5yYqIbyhLhH29vn4sHiTj7sU6GttvLMk3XwCmBXjo2k2j3zHqFUwh9RyFGF9VY6Z392Drf/E/cl+qOGypwULpg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.696.0.tgz", + "integrity": "sha512-vpVukqY3U2pb+ULeX0shs6L0aadNep6kKzjme/MyulPjtUDJpD3AekHsXRrCCGLmOqSKqRgQn5zhV9pQhHsb6Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -632,22 +632,22 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.688.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.688.0.tgz", - "integrity": "sha512-diIBWLpM5eg3sRggKSKGUJGBh8VyFo/wZLq80GSq1kxGlmJOMvwT6YvE+Z51xhEbYTKIjX9IH/NhO7W4pA3MNw==", + "version": "3.697.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.697.0.tgz", + "integrity": "sha512-K/y43P+NuHu5+21/29BoJSltcPekvcCU8i74KlGGHbW2Z105e5QVZlFjxivcPOjOA3gdC0W4SoFSIWam5RBhzw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.686.0", - "@aws-sdk/types": "3.686.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", "@smithy/is-array-buffer": "^3.0.0", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-stream": "^3.2.1", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-stream": "^3.3.1", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -656,14 +656,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.686.0.tgz", - "integrity": "sha512-+Yc6rO02z+yhFbHmRZGvEw1vmzf/ifS9a4aBjJGeVVU+ZxaUvnk+IUZWrj4YQopUQ+bSujmMUzJLXSkbDq7yuw==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.696.0.tgz", + "integrity": "sha512-zELJp9Ta2zkX7ELggMN9qMCgekqZhFC5V2rOr4hJDEb/Tte7gpfKSObAnw/3AYiVqt36sjHKfdkoTsuwGdEoDg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -671,13 +671,13 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.686.0.tgz", - "integrity": "sha512-pCLeZzt5zUGY3NbW4J/5x3kaHyJEji4yqtoQcUlJmkoEInhSxJ0OE8sTxAfyL3nIOF4yr6L2xdaLCqYgQT8Aog==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.696.0.tgz", + "integrity": "sha512-FgH12OB0q+DtTrP2aiDBddDKwL4BPOrm7w3VV9BJrSdkqQCNBPz8S1lb0y5eVH4tBG+2j7gKPlOv1wde4jF/iw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -685,13 +685,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.686.0.tgz", - "integrity": "sha512-cX43ODfA2+SPdX7VRxu6gXk4t4bdVJ9pkktbfnkE5t27OlwNfvSGGhnHrQL8xTOFeyQ+3T+oowf26gf1OI+vIg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.696.0.tgz", + "integrity": "sha512-KhkHt+8AjCxcR/5Zp3++YPJPpFQzxpr+jmONiT/Jw2yqnSngZ0Yspm5wGoRx2hS1HJbyZNuaOWEGuJoxLeBKfA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -699,14 +699,14 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.686.0.tgz", - "integrity": "sha512-jF9hQ162xLgp9zZ/3w5RUNhmwVnXDBlABEUX8jCgzaFpaa742qR/KKtjjZQ6jMbQnP+8fOCSXFAVNMU+s6v81w==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.696.0.tgz", + "integrity": "sha512-si/maV3Z0hH7qa99f9ru2xpS5HlfSVcasRlNUXKSDm611i7jFMWwGNLUOXFAOLhXotPX5G3Z6BLwL34oDeBMug==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -714,23 +714,23 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.687.0.tgz", - "integrity": "sha512-YGHYqiyRiNNucmvLrfx3QxIkjSDWR/+cc72bn0lPvqFUQBRHZgmYQLxVYrVZSmRzzkH2FQ1HsZcXhOafLbq4vQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@aws-sdk/util-arn-parser": "3.679.0", - "@smithy/core": "^2.5.1", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/protocol-http": "^4.1.5", - "@smithy/signature-v4": "^4.2.0", - "@smithy/smithy-client": "^3.4.2", - "@smithy/types": "^3.6.0", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.696.0.tgz", + "integrity": "sha512-M7fEiAiN7DBMHflzOFzh1I2MNSlLpbiH2ubs87bdRc2wZsDPSbs4l3v6h3WLhxoQK0bq6vcfroudrLBgvCuX3Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-arn-parser": "3.693.0", + "@smithy/core": "^2.5.3", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.7", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.8", - "@smithy/util-stream": "^3.2.1", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-stream": "^3.3.1", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -739,13 +739,13 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.686.0.tgz", - "integrity": "sha512-zJXml/CpVHFUdlGQqja87vNQ3rPB5SlDbfdwxlj1KBbjnRRwpBtxxmOlWRShg8lnVV6aIMGv95QmpIFy4ayqnQ==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.696.0.tgz", + "integrity": "sha512-w/d6O7AOZ7Pg3w2d3BxnX5RmGNWb5X4RNxF19rJqcgu/xqxxE/QwZTNd5a7eTsqLXAUIfbbR8hh0czVfC1pJLA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -753,17 +753,17 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.687.0.tgz", - "integrity": "sha512-nUgsKiEinyA50CaDXojAkOasAU3Apdg7Qox6IjNUC4ZjgOu7QWsCDB5N28AYMUt06cNYeYQdfMX1aEzG85a1Mg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.696.0.tgz", + "integrity": "sha512-Lvyj8CTyxrHI6GHd2YVZKIRI5Fmnugt3cpJo0VrKKEgK5zMySwEZ1n4dqPK6czYRWKd5+WnYHYAuU+Wdk6Jsjw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.686.0", - "@aws-sdk/types": "3.686.0", - "@aws-sdk/util-endpoints": "3.686.0", - "@smithy/core": "^2.5.1", - "@smithy/protocol-http": "^4.1.5", - "@smithy/types": "^3.6.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@smithy/core": "^2.5.3", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -771,16 +771,16 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.686.0.tgz", - "integrity": "sha512-6zXD3bSD8tcsMAVVwO1gO7rI1uy2fCD3czgawuPGPopeLiPpo6/3FoUWCQzk2nvEhj7p9Z4BbjwZGSlRkVrXTw==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.696.0.tgz", + "integrity": "sha512-7EuH142lBXjI8yH6dVS/CZeiK/WZsmb/8zP6bQbVYpMrppSTgB3MzZZdxVZGzL5r8zPQOU10wLC4kIMy0qdBVQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.8", + "@smithy/util-middleware": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -788,16 +788,16 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.687.0.tgz", - "integrity": "sha512-vdOQHCRHJPX9mT8BM6xOseazHD6NodvHl9cyF5UjNtLn+gERRJEItIA9hf0hlt62odGD8Fqp+rFRuqdmbNkcNw==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.696.0.tgz", + "integrity": "sha512-ijPkoLjXuPtgxAYlDoYls8UaG/VKigROn9ebbvPL/orEY5umedd3iZTcS9T+uAf4Ur3GELLxMQiERZpfDKaz3g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.687.0", - "@aws-sdk/types": "3.686.0", - "@smithy/protocol-http": "^4.1.5", - "@smithy/signature-v4": "^4.2.0", - "@smithy/types": "^3.6.0", + "@aws-sdk/middleware-sdk-s3": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/signature-v4": "^4.2.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -805,31 +805,31 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.686.0.tgz", - "integrity": "sha512-9oL4kTCSePFmyKPskibeiOXV6qavPZ63/kXM9Wh9V6dTSvBtLeNnMxqGvENGKJcTdIgtoqyqA6ET9u0PJ5IRIg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.696.0.tgz", + "integrity": "sha512-fvTcMADrkwRdNwVmJXi2pSPf1iizmUqczrR1KusH4XehI/KybS4U6ViskRT0v07vpxwL7x+iaD/8fR0PUu5L/g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/property-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.8", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" }, "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.686.0" + "@aws-sdk/client-sso-oidc": "^3.696.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.686.0.tgz", - "integrity": "sha512-xFnrb3wxOoJcW2Xrh63ZgFo5buIu9DF7bOHnwoUxHdNpUXicUh0AHw85TjXxyxIAd0d1psY/DU7QHoNI3OswgQ==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.696.0.tgz", + "integrity": "sha512-9rTvUJIAj5d3//U5FDPWGJ1nFJLuWb30vugGOrWk7aNZ6y9tuA3PI7Cc9dP8WEXKVyK1vuuk8rSFP2iqXnlgrw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -837,9 +837,9 @@ } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.679.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.679.0.tgz", - "integrity": "sha512-CwzEbU8R8rq9bqUFryO50RFBlkfufV9UfMArHPWlo+lmsC+NlSluHQALoj6Jkq3zf5ppn1CN0c1DDLrEqdQUXg==", + "version": "3.693.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.693.0.tgz", + "integrity": "sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -849,14 +849,14 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.686.0.tgz", - "integrity": "sha512-7msZE2oYl+6QYeeRBjlDgxQUhq/XRky3cXE0FqLFs2muLS7XSuQEXkpOXB3R782ygAP6JX0kmBxPTLurRTikZg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.696.0.tgz", + "integrity": "sha512-T5s0IlBVX+gkb9g/I6CLt4yAZVzMSiGnbUqWihWsHvQR1WOoIcndQy/Oz/IJXT9T2ipoy7a80gzV6a5mglrioA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/types": "^3.6.0", - "@smithy/util-endpoints": "^2.1.4", + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", + "@smithy/util-endpoints": "^2.1.6", "tslib": "^2.6.2" }, "engines": { @@ -875,27 +875,27 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.686.0.tgz", - "integrity": "sha512-YiQXeGYZegF1b7B2GOR61orhgv79qmI0z7+Agm3NXLO6hGfVV3kFUJbXnjtH1BgWo5hbZYW7HQ2omGb3dnb6Lg==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.696.0.tgz", + "integrity": "sha512-Z5rVNDdmPOe6ELoM5AhF/ja5tSjbe6ctSctDPb0JdDf4dT0v2MfwhJKzXju2RzX8Es/77Glh7MlaXLE0kCB9+Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.686.0", - "@smithy/types": "^3.6.0", + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.687.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.687.0.tgz", - "integrity": "sha512-idkP6ojSTZ4ek1pJ8wIN7r9U3KR5dn0IkJn3KQBXQ58LWjkRqLtft2vxzdsktWwhPKjjmIKl1S0kbvqLawf8XQ==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.696.0.tgz", + "integrity": "sha512-KhKqcfyXIB0SCCt+qsu4eJjsfiOrNzK5dCV7RAW2YIpp+msxGUUX0NdRE9rkzjiv+3EMktgJm3eEIS+yxtlVdQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.687.0", - "@aws-sdk/types": "3.686.0", - "@smithy/node-config-provider": "^3.1.9", - "@smithy/types": "^3.6.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -911,12 +911,12 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.686.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.686.0.tgz", - "integrity": "sha512-k0z5b5dkYSuOHY0AOZ4iyjcGBeVL9lWsQNF4+c+1oK3OW4fRWl/bNa1soMRMpangsHPzgyn/QkzuDbl7qR4qrw==", + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.696.0.tgz", + "integrity": "sha512-dn1mX+EeqivoLYnY7p2qLrir0waPnCgS/0YdRCAVU2x14FgfUYCH6Im3w3oi2dMwhxfKY5lYVB5NKvZu7uI9lQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -1298,12 +1298,12 @@ "license": "MIT" }, "node_modules/@smithy/abort-controller": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.8.tgz", - "integrity": "sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", + "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1330,15 +1330,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.12.tgz", - "integrity": "sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", + "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.11", - "@smithy/types": "^3.7.1", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.10", + "@smithy/util-middleware": "^3.0.11", "tslib": "^2.6.2" }, "engines": { @@ -1346,17 +1346,17 @@ } }, "node_modules/@smithy/core": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.4.tgz", - "integrity": "sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.5.tgz", + "integrity": "sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.10", - "@smithy/protocol-http": "^4.1.7", - "@smithy/types": "^3.7.1", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-stream": "^3.3.1", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.2", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -1365,15 +1365,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.7.tgz", - "integrity": "sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", + "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.11", - "@smithy/property-provider": "^3.1.10", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", "tslib": "^2.6.2" }, "engines": { @@ -1381,25 +1381,25 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.7.tgz", - "integrity": "sha512-kVSXScIiRN7q+s1x7BrQtZ1Aa9hvvP9FeCqCdBxv37GimIHgBCOnZ5Ip80HLt0DhnAKpiobFdGqTFgbaJNrazA==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.9.tgz", + "integrity": "sha512-F574nX0hhlNOjBnP+noLtsPFqXnWh2L0+nZKCwcu7P7J8k+k+rdIDs+RMnrMwrzhUE4mwMgyN0cYnEn0G8yrnQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.6.0", + "@smithy/types": "^3.7.1", "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.11.tgz", - "integrity": "sha512-Pd1Wnq3CQ/v2SxRifDUihvpXzirJYbbtXfEnnLV/z0OGCTx/btVX74P86IgrZkjOydOASBGXdPpupYQI+iO/6A==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.13.tgz", + "integrity": "sha512-Nee9m+97o9Qj6/XeLz2g2vANS2SZgAxV4rDBMKGHvFJHU/xz88x2RwCkwsvEwYjSX4BV1NG1JXmxEaDUzZTAtw==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.10", - "@smithy/types": "^3.6.0", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -1420,13 +1420,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.10.tgz", - "integrity": "sha512-hjpU1tIsJ9qpcoZq9zGHBJPBOeBGYt+n8vfhDwnITPhEre6APrvqq/y3XMDEGUT2cWQ4ramNqBPRbx3qn55rhw==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.12.tgz", + "integrity": "sha512-kiZymxXvZ4tnuYsPSMUHe+MMfc4FTeFWJIc0Q5wygJoUQM4rVHNghvd48y7ppuulNMbuYt95ah71pYc2+o4JOA==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.10", - "@smithy/types": "^3.6.0", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -1434,13 +1434,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.10.tgz", - "integrity": "sha512-ewG1GHbbqsFZ4asaq40KmxCmXO+AFSM1b+DcO2C03dyJj/ZH71CiTg853FSE/3SHK9q3jiYQIFjlGSwfxQ9kww==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.12.tgz", + "integrity": "sha512-1i8ifhLJrOZ+pEifTlF0EfZzMLUGQggYQ6WmZ4d5g77zEKf7oZ0kvh1yKWHPjofvOwqrkwRDVuxuYC8wVd662A==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^3.1.7", - "@smithy/types": "^3.6.0", + "@smithy/eventstream-codec": "^3.1.9", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -1448,14 +1448,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.1.tgz", - "integrity": "sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.2.tgz", + "integrity": "sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.7", - "@smithy/querystring-builder": "^3.0.10", - "@smithy/types": "^3.7.1", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } @@ -1473,12 +1473,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.10.tgz", - "integrity": "sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", + "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -1502,12 +1502,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.10.tgz", - "integrity": "sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", + "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" } }, @@ -1535,13 +1535,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.12.tgz", - "integrity": "sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", + "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.7", - "@smithy/types": "^3.7.1", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1549,18 +1549,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.4.tgz", - "integrity": "sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.5.tgz", + "integrity": "sha512-VhJNs/s/lyx4weiZdXSloBgoLoS8osV0dKIain8nGmx7of3QFKu5BSdEuk1z/U8x9iwes1i+XCiNusEvuK1ijg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^2.5.4", - "@smithy/middleware-serde": "^3.0.10", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.11", - "@smithy/types": "^3.7.1", - "@smithy/url-parser": "^3.0.10", - "@smithy/util-middleware": "^3.0.10", + "@smithy/core": "^2.5.5", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", "tslib": "^2.6.2" }, "engines": { @@ -1568,18 +1568,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.28", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.28.tgz", - "integrity": "sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^3.1.11", - "@smithy/protocol-http": "^4.1.7", - "@smithy/service-error-classification": "^3.0.10", - "@smithy/smithy-client": "^3.4.5", - "@smithy/types": "^3.7.1", - "@smithy/util-middleware": "^3.0.10", - "@smithy/util-retry": "^3.0.10", + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.30.tgz", + "integrity": "sha512-6323RL2BvAR3VQpTjHpa52kH/iSHyxd/G9ohb2MkBk2Ucu+oMtRXT8yi7KTSIS9nb58aupG6nO0OlXnQOAcvmQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -1588,12 +1588,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.10.tgz", - "integrity": "sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", + "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1601,12 +1601,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.10.tgz", - "integrity": "sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", + "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1614,14 +1614,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.11.tgz", - "integrity": "sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==", + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", + "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.10", - "@smithy/shared-ini-file-loader": "^3.1.11", - "@smithy/types": "^3.7.1", + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1629,15 +1629,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.1.tgz", - "integrity": "sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.2.tgz", + "integrity": "sha512-t4ng1DAd527vlxvOfKFYEe6/QFBcsj7WpNlWTyjorwXXcKw3XlltBGbyHfSJ24QT84nF+agDha9tNYpzmSRZPA==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.8", - "@smithy/protocol-http": "^4.1.7", - "@smithy/querystring-builder": "^3.0.10", - "@smithy/types": "^3.7.1", + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1645,12 +1645,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.10.tgz", - "integrity": "sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==", + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1658,12 +1658,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.7.tgz", - "integrity": "sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1671,12 +1671,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.10.tgz", - "integrity": "sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", + "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -1685,12 +1685,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.10.tgz", - "integrity": "sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", + "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1698,24 +1698,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.10.tgz", - "integrity": "sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1" + "@smithy/types": "^3.7.2" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.11.tgz", - "integrity": "sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==", + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", + "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1723,16 +1723,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.3.tgz", - "integrity": "sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", + "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.7", - "@smithy/types": "^3.7.1", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.10", + "@smithy/util-middleware": "^3.0.11", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -1742,17 +1742,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.5.tgz", - "integrity": "sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.5.0.tgz", + "integrity": "sha512-Y8FeOa7gbDfCWf7njrkoRATPa5eNLUEjlJS5z5rXatYuGkCb80LbHcu8AQR8qgAZZaNHCLyo2N+pxPsV7l+ivg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^2.5.4", - "@smithy/middleware-endpoint": "^3.2.4", - "@smithy/middleware-stack": "^3.0.10", - "@smithy/protocol-http": "^4.1.7", - "@smithy/types": "^3.7.1", - "@smithy/util-stream": "^3.3.1", + "@smithy/core": "^2.5.5", + "@smithy/middleware-endpoint": "^3.2.5", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.2", "tslib": "^2.6.2" }, "engines": { @@ -1760,9 +1760,9 @@ } }, "node_modules/@smithy/types": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.1.tgz", - "integrity": "sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -1772,13 +1772,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.10.tgz", - "integrity": "sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", + "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.10", - "@smithy/types": "^3.7.1", + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" } }, @@ -1843,14 +1843,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.28", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.28.tgz", - "integrity": "sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==", + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.30.tgz", + "integrity": "sha512-nLuGmgfcr0gzm64pqF2UT4SGWVG8UGviAdayDlVzJPNa6Z4lqvpDzdRXmLxtOdEjVlTOEdpZ9dd3ZMMu488mzg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.10", - "@smithy/smithy-client": "^3.4.5", - "@smithy/types": "^3.7.1", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -1859,17 +1859,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.28", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.28.tgz", - "integrity": "sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==", + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.30.tgz", + "integrity": "sha512-OD63eWoH68vp75mYcfYyuVH+p7Li/mY4sYOROnauDrtObo1cS4uWfsy/zhOTW8F8ZPxQC1ZXZKVxoxvMGUv2Ow==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.12", - "@smithy/credential-provider-imds": "^3.2.7", - "@smithy/node-config-provider": "^3.1.11", - "@smithy/property-provider": "^3.1.10", - "@smithy/smithy-client": "^3.4.5", - "@smithy/types": "^3.7.1", + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.5.0", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1877,13 +1877,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.6.tgz", - "integrity": "sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.11", - "@smithy/types": "^3.7.1", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1903,12 +1903,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.10.tgz", - "integrity": "sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", + "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.7.1", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1916,13 +1916,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.10.tgz", - "integrity": "sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.10", - "@smithy/types": "^3.7.1", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", "tslib": "^2.6.2" }, "engines": { @@ -1930,14 +1930,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.1.tgz", - "integrity": "sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.2.tgz", + "integrity": "sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^4.1.1", - "@smithy/node-http-handler": "^3.3.1", - "@smithy/types": "^3.7.1", + "@smithy/fetch-http-handler": "^4.1.2", + "@smithy/node-http-handler": "^3.3.2", + "@smithy/types": "^3.7.2", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", diff --git a/lib/local-constructs/clamav-scanning/package.json b/lib/local-constructs/clamav-scanning/package.json index f570a7aa15..d13b45a566 100644 --- a/lib/local-constructs/clamav-scanning/package.json +++ b/lib/local-constructs/clamav-scanning/package.json @@ -19,7 +19,7 @@ "esbuild": "^0.21.5" }, "dependencies": { - "@aws-sdk/client-s3": "^3.688.0", + "@aws-sdk/client-s3": "^3.697.0", "@types/mime-types": "^2.1.4", "file-type": "^19.6.0", "mime-types": "^2.1.35", diff --git a/lib/packages/shared-types/events/app-k.ts b/lib/packages/shared-types/events/app-k.ts index d0a46ff82d..cf258c5589 100644 --- a/lib/packages/shared-types/events/app-k.ts +++ b/lib/packages/shared-types/events/app-k.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { attachmentArraySchema } from "../attachments"; +import { attachmentArraySchema, attachmentArraySchemaOptional } from "../attachments"; export const baseSchema = z.object({ id: z @@ -15,12 +15,12 @@ export const baseSchema = z.object({ title: z.string().trim().min(1, { message: "Required" }).max(125), attachments: z.object({ appk: z.object({ - label: z.string().default("Appendix K Template"), - files: attachmentArraySchema({ max: 1 }), + label: z.string().default("1915(c) Appendix K Amendment Waiver Template"), + files: attachmentArraySchema(), }), other: z.object({ label: z.string().default("Other"), - files: attachmentArraySchema({ max: 1 }), + files: attachmentArraySchemaOptional(), }), }), additionalInformation: z.string().max(4000).nullable().default(null).optional(), diff --git a/lib/packages/shared-types/events/capitated-renewal.test.ts b/lib/packages/shared-types/events/capitated-renewal.test.ts new file mode 100644 index 0000000000..3fbeb89ca6 --- /dev/null +++ b/lib/packages/shared-types/events/capitated-renewal.test.ts @@ -0,0 +1,62 @@ +import { events } from "shared-types/events"; +import { describe, expect, test } from "vitest"; + +describe("Capitated Renewal", () => { + const schema = events["capitated-renewal"].baseSchema.pick({ id: true }); + const formatErrorMessage = "Renewal Number must be in the format of"; + + test("VALID ID", () => { + const validId = "MD-2024.R01.00"; + const result = schema.safeParse({ id: validId }); + + expect(result.success).toBe(true); + }); + + test("EMPTY ID", () => { + const validId = ""; + const result = schema.safeParse({ id: validId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toBe("Required"); + }); + + test("TOO SHORT ID FORMAT", () => { + const invalidId = "MD-202.R01.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("TOO LONG ID FORMAT", () => { + const invalidId = "MD-123456.R01.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("INVALID CHARACTERS ID FORMAT", () => { + const invalidId = "MD-1234.R0a.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("INVALID R## ID FORMAT", () => { + const invalidId = "NY-1234.R00.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("INVALID AMENDMENT ID", () => { + const invalidId = "NY-1234.R01.01"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); +}); diff --git a/lib/packages/shared-types/events/capitated-renewal.ts b/lib/packages/shared-types/events/capitated-renewal.ts index 6bb93c5318..0fffe6e7f7 100644 --- a/lib/packages/shared-types/events/capitated-renewal.ts +++ b/lib/packages/shared-types/events/capitated-renewal.ts @@ -1,8 +1,5 @@ import { z } from "zod"; -import { - attachmentArraySchema, - attachmentArraySchemaOptional, -} from "../attachments"; +import { attachmentArraySchema, attachmentArraySchemaOptional } from "../attachments"; export const baseSchema = z.object({ event: z.literal("capitated-renewal").default("capitated-renewal"), @@ -10,26 +7,20 @@ export const baseSchema = z.object({ id: z .string() .min(1, { message: "Required" }) - .refine((id) => /^[A-Z]{2}-\d{4,5}\.R(?!00)\d{2}\.\d{2}$/.test(id), { + .refine((id) => /^[A-Z]{2}-\d{4,5}\.R(?!00)\d{2}\.[0]{2}$/.test(id), { message: - "Renewal Number must be in the format of SS-####.R##.00 or SS-#####.R##.00 For renewals, the “R##” starts with '01' and ascends.", + "The 1915(b) Waiver Renewal Number must be in the format of SS-####.R##.00 or SS-#####.R##.00. For renewals, the “R##” starts with ‘01’ and ascends.", }), proposedEffectiveDate: z.number(), attachments: z.object({ bCapWaiverApplication: z.object({ - label: z - .string() - .default( - "1915(b) Comprehensive (Capitated) Waiver Application Pre-print", - ), + label: z.string().default("1915(b) Comprehensive (Capitated) Waiver Application Pre-print"), files: attachmentArraySchema(), }), bCapCostSpreadsheets: z.object({ label: z .string() - .default( - "1915(b) Comprehensive (Capitated) Waiver Cost Effectiveness Spreadsheets", - ), + .default("1915(b) Comprehensive (Capitated) Waiver Cost Effectiveness Spreadsheets"), files: attachmentArraySchema(), }), bCapIndependentAssessment: z.object({ @@ -49,12 +40,7 @@ export const baseSchema = z.object({ files: attachmentArraySchemaOptional(), }), }), - additionalInformation: z - .string() - .max(4000) - .nullable() - .default(null) - .optional(), + additionalInformation: z.string().max(4000).nullable().default(null).optional(), waiverNumber: z .string() .min(1, { message: "Required" }) diff --git a/lib/packages/shared-types/events/contracting-renewal.test.ts b/lib/packages/shared-types/events/contracting-renewal.test.ts new file mode 100644 index 0000000000..d24086c2ff --- /dev/null +++ b/lib/packages/shared-types/events/contracting-renewal.test.ts @@ -0,0 +1,62 @@ +import { events } from "shared-types/events"; +import { describe, expect, test } from "vitest"; + +describe("Contracting Renewal", () => { + const schema = events["contracting-renewal"].baseSchema.pick({ id: true }); + const formatErrorMessage = "Renewal Number must be in the format of"; + + test(" VALID ID", () => { + const validId = "MD-2024.R01.00"; + const result = schema.safeParse({ id: validId }); + + expect(result.success).toBe(true); + }); + + test("EMPTY ID", () => { + const validId = ""; + const result = schema.safeParse({ id: validId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toBe("Required"); + }); + + test("TOO SHORT ID FORMAT", () => { + const invalidId = "MD-202.R01.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("TOO LONG ID FORMAT", () => { + const invalidId = "MD-123456.R01.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("INVALID CHARACTERS ID FORMAT", () => { + const invalidId = "MD-1234.R0a.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("INVALID R## ID FORMAT", () => { + const invalidId = "NY-1234.R00.00"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); + + test("INVALID AMENDMENT ID", () => { + const invalidId = "NY-1234.R01.01"; + const result = schema.safeParse({ id: invalidId }); + + expect(result.success).toBe(false); + expect(result.error.issues[0].message).toMatch(formatErrorMessage); + }); +}); diff --git a/lib/packages/shared-types/events/contracting-renewal.ts b/lib/packages/shared-types/events/contracting-renewal.ts index bbb87d1b4c..5bee7b66c5 100644 --- a/lib/packages/shared-types/events/contracting-renewal.ts +++ b/lib/packages/shared-types/events/contracting-renewal.ts @@ -1,8 +1,5 @@ import { z } from "zod"; -import { - attachmentArraySchema, - attachmentArraySchemaOptional, -} from "../attachments"; +import { attachmentArraySchema, attachmentArraySchemaOptional } from "../attachments"; export const baseSchema = z.object({ event: z.literal("contracting-renewal").default("contracting-renewal"), @@ -10,18 +7,16 @@ export const baseSchema = z.object({ id: z .string() .min(1, { message: "Required" }) - .refine((id) => /^[A-Z]{2}-\d{4,5}\.R(?!00)\d{2}\.\d{2}$/.test(id), { + .refine((id) => /^[A-Z]{2}-\d{4,5}\.R(?!00)\d{2}\.[0]{2}$/.test(id), { message: - "Renewal Number must be in the format of SS-####.R##.00 or SS-#####.R##.00 For renewals, the “R##” starts with '01' and ascends.", + "The 1915(b) Waiver Renewal Number must be in the format of SS-####.R##.00 or SS-#####.R##.00. For renewals, the “R##” starts with ‘01’ and ascends.", }), proposedEffectiveDate: z.number(), attachments: z.object({ b4WaiverApplication: z.object({ label: z .string() - .default( - "1915(b)(4) FFS Selective Contracting (Streamlined) Waiver Application Pre-print", - ), + .default("1915(b)(4) FFS Selective Contracting (Streamlined) Waiver Application Pre-print"), files: attachmentArraySchema(), }), b4IndependentAssessment: z.object({ @@ -41,12 +36,7 @@ export const baseSchema = z.object({ files: attachmentArraySchemaOptional(), }), }), - additionalInformation: z - .string() - .max(4000) - .nullable() - .default(null) - .optional(), + additionalInformation: z.string().max(4000).nullable().default(null).optional(), waiverNumber: z .string() .min(1, { message: "Required" }) diff --git a/lib/packages/shared-types/events/upload-subsequent-documents.ts b/lib/packages/shared-types/events/upload-subsequent-documents.ts index 9cf2e90df7..4f83a47ad7 100644 --- a/lib/packages/shared-types/events/upload-subsequent-documents.ts +++ b/lib/packages/shared-types/events/upload-subsequent-documents.ts @@ -3,7 +3,7 @@ import { attachmentArraySchemaOptional } from "../attachments"; export const baseSchema = z.object({ event: z.literal("upload-subsequent-documents").default("upload-subsequent-documents"), - additionalInformation: z.string().max(4000).default(null), + additionalInformation: z.string().max(4000), attachments: z.record( z.string(), z.object({ diff --git a/lib/packages/shared-types/opensearch/_.ts b/lib/packages/shared-types/opensearch/_.ts index 17743587c8..83788bcd9d 100644 --- a/lib/packages/shared-types/opensearch/_.ts +++ b/lib/packages/shared-types/opensearch/_.ts @@ -38,13 +38,7 @@ export type FilterType = | "exists"; export type RangeValue = { gte?: string; lte?: string }; -export type FilterValue = - | string - | string[] - | number - | boolean - | RangeValue - | null; +export type FilterValue = string | string[] | number | boolean | RangeValue | null; export type Filterable<_FIELD> = { type: FilterType; @@ -85,7 +79,7 @@ export type ExportHeaderOptions = { name: string; }; -type BaseIndex = +export type BaseIndex = | "main" | "insights" | "changelog" diff --git a/lib/packages/shared-types/opensearch/changelog/index.ts b/lib/packages/shared-types/opensearch/changelog/index.ts index d048bdbd06..91cea308f2 100644 --- a/lib/packages/shared-types/opensearch/changelog/index.ts +++ b/lib/packages/shared-types/opensearch/changelog/index.ts @@ -1,46 +1,86 @@ +import { z } from "zod"; import { - Response as Res, - Hit, - Filterable as FIL, - QueryState, AggQuery, ExportHeaderOptions, + Filterable as FIL, + Hit, + QueryState, + Response as Res, } from "./../_"; -import { z } from "zod"; import { + appK, capitatedAmendment, capitatedInitial, capitatedRenewal, contractingAmendment, contractingInitial, contractingRenewal, + legacyAdminChange, + legacyEvent, newChipSubmission, newMedicaidSubmission, + respondToRai, temporaryExtension, - withdrawPackage, - withdrawRai, toggleWithdrawRai, - respondToRai, - appK, uploadSubsequentDocuments, + withdrawPackage, + withdrawRai, } from "./transforms"; -// legacy -import { legacyAdminChange, legacyEvent } from "./transforms"; +export type AppkDocument = z.infer; +export type CapitatedAmendmentDocument = z.infer; +export type CapitatedInitialDocument = z.infer; +export type CapitatedRenewalDocument = z.infer; +export type ContractingAmendmentDocument = z.infer; +export type ContractingInitialDocument = z.infer; +export type ContractingRenewalDocument = z.infer; +export type LegacyEventDocument = z.infer; +export type LegacyAdminChangeDocument = z.infer; +export type NewChipSubmissionDocument = z.infer; +export type NewMedicaidSubmissionDocument = z.infer; +export type RespondToRaiDocument = z.infer; +export type TemporaryExtensionDocument = z.infer; +export type ToggleWithdrawRaiDocument = z.infer; +export type UploadSubsequentDocuments = z.infer; +export type WithdrawPackageDocument = z.infer; +export type WithdrawRaiDocument = z.infer; -export type Document = z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer; +export type Document = Omit & + Omit & + Omit & + Omit & + Omit & + Omit & + Omit & + LegacyEventDocument & + LegacyAdminChangeDocument & + Omit & + Omit & + Omit & + Omit & + Omit & + Omit & + Omit & + Omit & { + event: + | "app-k" + | "capitated-amendment" + | "capitated-initial" + | "capitated-renewal" + | "contracting-amendment" + | "contracting-initial" + | "contracting-renewal" + | "legacy-admin-change" + | "new-chip-submission" + | "new-medicaid-submission" + | "respond-to-rai" + | "temporary-extension" + | "toggle-withdraw-rai" + | "upload-subsequent-documents" + | "withdraw-package" + | "withdraw-rai"; + }; export type Response = Res; export type ItemResult = Hit & { @@ -56,19 +96,20 @@ export type ExportHeader = ExportHeaderOptions; export * from "./transforms"; export const transforms = { + "app-k": appK, "capitated-amendment": capitatedAmendment, "capitated-initial": capitatedInitial, "capitated-renewal": capitatedRenewal, "contracting-amendment": contractingAmendment, "contracting-initial": contractingInitial, "contracting-renewal": contractingRenewal, + "legacy-admin-change": legacyAdminChange, "new-chip-submission": newChipSubmission, "new-medicaid-submission": newMedicaidSubmission, + "respond-to-rai": respondToRai, "temporary-extension": temporaryExtension, - "withdraw-package": withdrawPackage, - "withdraw-rai": withdrawRai, "toggle-withdraw-rai": toggleWithdrawRai, - "respond-to-rai": respondToRai, - "app-k": appK, "upload-subsequent-documents": uploadSubsequentDocuments, + "withdraw-package": withdrawPackage, + "withdraw-rai": withdrawRai, }; diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/app-k.ts b/lib/packages/shared-types/opensearch/changelog/transforms/app-k.ts index 6799024526..e3ccd697f7 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/app-k.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/app-k.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["app-k"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/capitated-amendment.ts b/lib/packages/shared-types/opensearch/changelog/transforms/capitated-amendment.ts index b5cf407acd..c40fd1d12e 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/capitated-amendment.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/capitated-amendment.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["capitated-amendment"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/capitated-initial.ts b/lib/packages/shared-types/opensearch/changelog/transforms/capitated-initial.ts index a5d1334b2b..a90fd61c17 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/capitated-initial.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/capitated-initial.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["capitated-initial"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/capitated-renewal.ts b/lib/packages/shared-types/opensearch/changelog/transforms/capitated-renewal.ts index e8b4268725..da837c11bb 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/capitated-renewal.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/capitated-renewal.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["capitated-renewal"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/contracting-amendment.ts b/lib/packages/shared-types/opensearch/changelog/transforms/contracting-amendment.ts index e2668bd7b3..80cfab1204 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/contracting-amendment.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/contracting-amendment.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["contracting-amendment"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/contracting-initial.ts b/lib/packages/shared-types/opensearch/changelog/transforms/contracting-initial.ts index 5e27912f1b..ee021cc3b1 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/contracting-initial.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/contracting-initial.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["contracting-initial"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/contracting-renewal.ts b/lib/packages/shared-types/opensearch/changelog/transforms/contracting-renewal.ts index c6ec0114dc..4b4cfac68c 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/contracting-renewal.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/contracting-renewal.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["contracting-renewal"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/legacy-event.ts b/lib/packages/shared-types/opensearch/changelog/transforms/legacy-event.ts index 04a2879417..b99a9eb0a0 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/legacy-event.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/legacy-event.ts @@ -1,9 +1,9 @@ -import { legacyEventSchema, handleLegacyAttachment, Action } from "../../.."; +import { Action, handleLegacyAttachment, legacyEventSchema } from "../../.."; export const transform = (id: string) => { return legacyEventSchema.transform((data) => { // Resolve the action type based on the GSI1pk - const eventType = data.GSI1pk.split("OneMAC#submit")[1]; + const eventType = data?.GSI1pk?.split("OneMAC#submit")?.[1] || ""; let actionType; diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/new-chip-submission.ts b/lib/packages/shared-types/opensearch/changelog/transforms/new-chip-submission.ts index bb21080c7b..f9ff4da973 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/new-chip-submission.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/new-chip-submission.ts @@ -2,28 +2,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["new-chip-submission"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/new-medicaid-submission.ts b/lib/packages/shared-types/opensearch/changelog/transforms/new-medicaid-submission.ts index 7382c33683..fa2966824c 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/new-medicaid-submission.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/new-medicaid-submission.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["new-medicaid-submission"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/respond-to-rai.ts b/lib/packages/shared-types/opensearch/changelog/transforms/respond-to-rai.ts index b846dfd37a..887bb3bb17 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/respond-to-rai.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/respond-to-rai.ts @@ -1,34 +1,36 @@ import { events } from "shared-types"; export const transform = (offset: number) => { - return events["respond-to-rai"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); - return { - ...data, - attachments, - packageId: data.id, - id: `${data.id}-${offset}`, - }; - }); + return events["respond-to-rai"].schema.transform((data) => { + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; + return { + ...data, + attachments, + packageId: data.id, + id: `${data.id}-${offset}`, + }; + }); }; -export type Schema = ReturnType; \ No newline at end of file +export type Schema = ReturnType; diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/temporary-extension.ts b/lib/packages/shared-types/opensearch/changelog/transforms/temporary-extension.ts index 3ad42d0352..33b14cc171 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/temporary-extension.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/temporary-extension.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["temporary-extension"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/upload-subsequent-documents.ts b/lib/packages/shared-types/opensearch/changelog/transforms/upload-subsequent-documents.ts index 2bbbe7a949..968afafc42 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/upload-subsequent-documents.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/upload-subsequent-documents.ts @@ -1,27 +1,23 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["upload-subsequent-documents"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - ([, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap(([, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if (attachment?.files && Array.isArray(attachment.files) && attachment.files.length > 0) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-package.ts b/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-package.ts index 44bd72cd53..ad1ed87807 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-package.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-package.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["withdraw-package"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-rai.ts b/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-rai.ts index dfc419f016..e167bd821a 100644 --- a/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-rai.ts +++ b/lib/packages/shared-types/opensearch/changelog/transforms/withdraw-rai.ts @@ -1,28 +1,30 @@ import { events } from "shared-types"; export const transform = (offset: number) => { return events["withdraw-rai"].schema.transform((data) => { - const attachments = Object.entries(data.attachments).flatMap( - //eslint-disable-next-line - ([_title, attachment]) => { - // Check if 'attachment' exists and has a non-empty 'files' array - if ( - attachment && - Array.isArray(attachment.files) && - attachment.files.length > 0 - ) { - // Map each file in 'files' array to the desired shape - return attachment.files.map((file) => ({ - filename: file.filename, - title: attachment.label, - bucket: file.bucket, - key: file.key, - uploadDate: file.uploadDate, - })); - } - // If there are no files or the files array is empty, return an empty array - return []; - }, - ); + const attachments = data.attachments + ? Object.entries(data.attachments).flatMap( + //eslint-disable-next-line + ([_title, attachment]) => { + // Check if 'attachment' exists and has a non-empty 'files' array + if ( + attachment?.files && + Array.isArray(attachment.files) && + attachment.files.length > 0 + ) { + // Map each file in 'files' array to the desired shape + return attachment.files.map((file) => ({ + filename: file.filename, + title: attachment.label, + bucket: file.bucket, + key: file.key, + uploadDate: file.uploadDate, + })); + } + // If there are no files or the files array is empty, return an empty array + return []; + }, + ) + : []; return { ...data, attachments, diff --git a/lib/packages/shared-types/opensearch/main/index.ts b/lib/packages/shared-types/opensearch/main/index.ts index 805ddbe3ab..7e54f3bee3 100644 --- a/lib/packages/shared-types/opensearch/main/index.ts +++ b/lib/packages/shared-types/opensearch/main/index.ts @@ -1,45 +1,64 @@ -import { Response as Res, Hit, Filterable as FIL, QueryState, AggQuery } from "./../_"; import { z } from "zod"; -import { ItemResult as Changelog } from "./../changelog"; +import { ItemResult as Changelog } from "../changelog"; +import { AggQuery, Filterable as FIL, Hit, QueryState, Response as Res } from "./../_"; import { - capitatedInitial, + appK, capitatedAmendment, + capitatedInitial, capitatedRenewal, - contractingInitial, + changedDate, contractingAmendment, + contractingInitial, contractingRenewal, + legacyPackageView, newChipSubmission, newMedicaidSubmission, - legacyPackageView, - withdrawPackage, respondToRai, - withdrawRai, - toggleWithdrawRai, seatool, - changedDate, temporaryExtension, - appK, + toggleWithdrawRai, uploadSubsequentDocuments, + withdrawPackage, + withdrawRai, } from "./transforms"; -export type Document = z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & - z.infer & { +export type AppkDocument = z.infer; +export type CapitatedAmendmentDocument = z.infer; +export type CapitatedInitialDocument = z.infer; +export type CapitatedRenewalDocument = z.infer; +export type ChangedDateDocument = z.infer; +export type ContractingAmendmentDocument = z.infer; +export type ContractingInitialDocument = z.infer; +export type ContractingRenewalDocument = z.infer; +export type LegacyPackageViewDocument = z.infer; +export type NewChipSubmissionDocument = z.infer; +export type NewMedicaidSubmissionDocument = z.infer; +export type RespondToRaiDocument = z.infer; +export type SeatoolDocument = z.infer; +export type TemporaryExtensionDocument = z.infer; +export type ToggleWithdrawRaiDocument = z.infer; +export type UploadSubsequentDocuments = z.infer; +export type WithdrawPackageDocument = z.infer; +export type WithdrawRaiDocument = z.infer; + +export type Document = AppkDocument & + CapitatedAmendmentDocument & + CapitatedInitialDocument & + CapitatedRenewalDocument & + ChangedDateDocument & + ContractingAmendmentDocument & + ContractingInitialDocument & + ContractingRenewalDocument & + LegacyPackageViewDocument & + NewChipSubmissionDocument & + NewMedicaidSubmissionDocument & + RespondToRaiDocument & + SeatoolDocument & + TemporaryExtensionDocument & + ToggleWithdrawRaiDocument & + UploadSubsequentDocuments & + WithdrawPackageDocument & + WithdrawRaiDocument & { makoChangedDate: string; changelog?: Changelog[]; appkChildren?: Omit[]; @@ -58,19 +77,19 @@ export type Aggs = AggQuery; export * from "./transforms"; export const transforms = { - "new-chip-submission": newChipSubmission, - "new-medicaid-submission": newMedicaidSubmission, - "capitated-initial": capitatedInitial, + "app-k": appK, "capitated-amendment": capitatedAmendment, + "capitated-initial": capitatedInitial, "capitated-renewal": capitatedRenewal, "contracting-amendment": contractingAmendment, "contracting-initial": contractingInitial, "contracting-renewal": contractingRenewal, + "new-chip-submission": newChipSubmission, + "new-medicaid-submission": newMedicaidSubmission, + "respond-to-rai": respondToRai, "temporary-extension": temporaryExtension, - "withdraw-package": withdrawPackage, - "withdraw-rai": withdrawRai, "toggle-withdraw-rai": toggleWithdrawRai, - "respond-to-rai": respondToRai, - "app-k": appK, "upload-subsequent-documents": uploadSubsequentDocuments, + "withdraw-package": withdrawPackage, + "withdraw-rai": withdrawRai, }; diff --git a/lib/packages/shared-types/opensearch/main/transforms/app-k.ts b/lib/packages/shared-types/opensearch/main/transforms/app-k.ts index 2df7b1c419..f057d73e49 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/app-k.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/app-k.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -7,7 +7,7 @@ import { export const transform = () => { return events["app-k"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); @@ -15,20 +15,20 @@ export const transform = () => { title: data.title, additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/capitated-amendment.ts b/lib/packages/shared-types/opensearch/main/transforms/capitated-amendment.ts index 7ece9cc3fd..8e71ee3842 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/capitated-amendment.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/capitated-amendment.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["capitated-amendment"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/capitated-initial.ts b/lib/packages/shared-types/opensearch/main/transforms/capitated-initial.ts index f031ca0f6a..0bdf7e9f1e 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/capitated-initial.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/capitated-initial.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["capitated-initial"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/capitated-renewal.ts b/lib/packages/shared-types/opensearch/main/transforms/capitated-renewal.ts index 3e26162332..683db73b51 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/capitated-renewal.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/capitated-renewal.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["capitated-renewal"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/changedDate.ts b/lib/packages/shared-types/opensearch/main/transforms/changedDate.ts index f0aec6a2e9..372261695b 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/changedDate.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/changedDate.ts @@ -1,23 +1,11 @@ -import { seaChangedDateSchema } from "../../.."; +import { seatoolRecordWithUpdatedDateSchema } from "../../../seatool-tables"; export const transform = () => { - return seaChangedDateSchema.transform((data) => { - const transformedData = { - id: data.ID_Number, - changedDate: - data.Changed_Date !== null && data.Changed_Date !== undefined - ? new Date(data.Changed_Date).toISOString() - : null, - }; - return transformedData; - }); + return seatoolRecordWithUpdatedDateSchema.transform((data) => ({ + id: data.ID_Number, + changedDate: + typeof data.Changed_Date === "number" ? new Date(data.Changed_Date).toISOString() : null, + })); }; export type Schema = ReturnType; - -export const tombstone = (id: string) => { - return { - id, - changedDate: null, - }; -}; diff --git a/lib/packages/shared-types/opensearch/main/transforms/contracting-amendment.ts b/lib/packages/shared-types/opensearch/main/transforms/contracting-amendment.ts index af5c1bc12b..9305fb6daa 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/contracting-amendment.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/contracting-amendment.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["contracting-amendment"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/contracting-initial.ts b/lib/packages/shared-types/opensearch/main/transforms/contracting-initial.ts index 02af6c1909..795732cbae 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/contracting-initial.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/contracting-initial.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["contracting-initial"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/contracting-renewal.ts b/lib/packages/shared-types/opensearch/main/transforms/contracting-renewal.ts index 897edee867..e3257de90f 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/contracting-renewal.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/contracting-renewal.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["contracting-renewal"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/issue-rai.ts b/lib/packages/shared-types/opensearch/main/transforms/issue-rai.ts deleted file mode 100644 index fe5f7925af..0000000000 --- a/lib/packages/shared-types/opensearch/main/transforms/issue-rai.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { raiIssueSchema } from "../../.."; - -export const transform = (id: string) => { - return raiIssueSchema.transform((data) => ({ - id, - makoChangedDate: data.timestamp - ? new Date(data.timestamp).toISOString() - : null, - })); -}; - -export type Schema = ReturnType; diff --git a/lib/packages/shared-types/opensearch/main/transforms/legacy-package-view.ts b/lib/packages/shared-types/opensearch/main/transforms/legacy-package-view.ts index 40900891c4..5e72280352 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/legacy-package-view.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/legacy-package-view.ts @@ -10,9 +10,7 @@ export const transform = (id: string) => { const noso = isLegacyNoso(data); if (data.submitterName === "-- --" && !noso) return undefined; // This is used to handle legacy hard deletes - const legacySubmissionTimestamp = getDateStringOrNullFromEpoc( - data.submissionTimestamp, - ); + const legacySubmissionTimestamp = getDateStringOrNullFromEpoc(data.submissionTimestamp); if (data.componentType?.startsWith("waiverextension")) { return { id, @@ -58,9 +56,7 @@ export const tombstone = (id: string) => { }; const getDateStringOrNullFromEpoc = (epocDate: number | null | undefined) => - epocDate !== null && epocDate !== undefined - ? new Date(epocDate).toISOString() - : null; + epocDate !== null && epocDate !== undefined ? new Date(epocDate)?.toISOString() : null; function isLegacyNoso(record: LegacyPackageAction): boolean { return ( diff --git a/lib/packages/shared-types/opensearch/main/transforms/new-chip-submission.ts b/lib/packages/shared-types/opensearch/main/transforms/new-chip-submission.ts index 18a83f2060..2e71269e91 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/new-chip-submission.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/new-chip-submission.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["new-chip-submission"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/new-medicaid-submission.ts b/lib/packages/shared-types/opensearch/main/transforms/new-medicaid-submission.ts index be258aafa0..bdb6d3922f 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/new-medicaid-submission.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/new-medicaid-submission.ts @@ -1,4 +1,4 @@ -import { events, SEATOOL_STATUS, getStatus } from "shared-types"; +import { events, getStatus, SEATOOL_STATUS } from "shared-types"; import { getNextBusinessDayTimestamp, seaToolFriendlyTimestamp, @@ -8,27 +8,27 @@ export const transform = () => { // any adhoc logic return events["new-medicaid-submission"].schema.transform((data) => { const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING); - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus, description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus, - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, proposedDate: data.proposedEffectiveDate, // wish this was proposedEffectiveDate subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, initialIntakeNeeded: true, diff --git a/lib/packages/shared-types/opensearch/main/transforms/remove-appk-child.ts b/lib/packages/shared-types/opensearch/main/transforms/remove-appk-child.ts index 6556688a92..42576f1e1a 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/remove-appk-child.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/remove-appk-child.ts @@ -6,9 +6,7 @@ export const transform = (id: string) => { id, appkParentId: null, appkParent: true, - makoChangedDate: data.timestamp - ? new Date(data.timestamp).toISOString() - : null, + makoChangedDate: data.timestamp ? new Date(data.timestamp).toISOString() : null, }; }); }; diff --git a/lib/packages/shared-types/opensearch/main/transforms/seatool.ts b/lib/packages/shared-types/opensearch/main/transforms/seatool.ts index be13d4f8d4..94a4e9265c 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/seatool.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/seatool.ts @@ -1,11 +1,11 @@ import { - seatoolSchema, - SEATOOL_STATUS, - getStatus, finalDispositionStatuses, + getStatus, SeaTool, - SeatoolOfficer, SEATOOL_SPW_STATUS, + SEATOOL_STATUS, + SeatoolOfficer, + seatoolSchema, } from "../../.."; import { Authority, SEATOOL_AUTHORITIES } from "shared-types"; @@ -24,9 +24,9 @@ function getLeadAnalyst(eventData: SeaTool) { ); if (leadAnalyst) { - leadAnalystOfficerId = leadAnalyst.OFFICER_ID; - leadAnalystName = `${leadAnalyst.FIRST_NAME} ${leadAnalyst.LAST_NAME}`; - leadAnalystEmail = leadAnalyst.EMAIL; + leadAnalystOfficerId = leadAnalyst.OFFICER_ID || null; + leadAnalystName = `${leadAnalyst.FIRST_NAME || ""} ${leadAnalyst.LAST_NAME || ""}`; + leadAnalystEmail = leadAnalyst.EMAIL || null; } } return { @@ -72,17 +72,15 @@ const getRaiDate = (data: SeaTool) => { }; const getDateStringOrNullFromEpoc = (epocDate: number | null | undefined) => - epocDate !== null && epocDate !== undefined - ? new Date(epocDate).toISOString() - : null; + epocDate !== null && epocDate !== undefined ? new Date(epocDate).toISOString() : null; const compileSrtList = ( officers: SeatoolOfficer[] | null | undefined, ): { name: string; email: string }[] => officers?.length ? officers.map((o) => ({ - name: `${o.FIRST_NAME} ${o.LAST_NAME}`, - email: o.EMAIL, + name: `${o.FIRST_NAME || ""} ${o.LAST_NAME || ""}`, + email: o.EMAIL || "", })) : []; @@ -113,43 +111,33 @@ const isInSecondClock = ( return false; // otherwise, we're not }; -const getAuthority = (authorityId: number | null) => { - try { - if (!authorityId) return null; - return SEATOOL_AUTHORITIES[authorityId]; - } catch (error) { - console.log(`SEATOOL AUTHORITY LOOKUP ERROR: ${authorityId}`); - console.log(error); - return null; - } -}; - export const transform = (id: string) => { return seatoolSchema.transform((data) => { - const { leadAnalystName, leadAnalystOfficerId, leadAnalystEmail } = - getLeadAnalyst(data); - const { raiReceivedDate, raiRequestedDate, raiWithdrawnDate } = - getRaiDate(data); - const seatoolStatus = data.STATE_PLAN.SPW_STATUS_ID - ? SEATOOL_SPW_STATUS[data.STATE_PLAN.SPW_STATUS_ID] + const { leadAnalystName, leadAnalystOfficerId, leadAnalystEmail } = getLeadAnalyst(data); + const { raiReceivedDate, raiRequestedDate, raiWithdrawnDate } = getRaiDate(data); + const seatoolStatus = data?.STATE_PLAN?.SPW_STATUS_ID + ? SEATOOL_SPW_STATUS[data?.STATE_PLAN?.SPW_STATUS_ID] : "Unknown"; + const authority = + data?.STATE_PLAN?.PLAN_TYPE && data.STATE_PLAN.PLAN_TYPE in SEATOOL_AUTHORITIES + ? SEATOOL_AUTHORITIES[data?.STATE_PLAN?.PLAN_TYPE] + : null; + const { stateStatus, cmsStatus } = getStatus(seatoolStatus); const resp = { id, actionType: data.ACTIONTYPES?.[0].ACTION_NAME, approvedEffectiveDate: getDateStringOrNullFromEpoc( - data.STATE_PLAN.APPROVED_EFFECTIVE_DATE || - data.STATE_PLAN.ACTUAL_EFFECTIVE_DATE, + data.STATE_PLAN.APPROVED_EFFECTIVE_DATE || data.STATE_PLAN.ACTUAL_EFFECTIVE_DATE, ), changed_date: data.STATE_PLAN.CHANGED_DATE, description: data.STATE_PLAN.SUMMARY_MEMO, finalDispositionDate: getFinalDispositionDate(seatoolStatus, data), leadAnalystOfficerId, leadAnalystEmail, - initialIntakeNeeded: - !leadAnalystName && !finalDispositionStatuses.includes(seatoolStatus), + initialIntakeNeeded: !leadAnalystName && !finalDispositionStatuses.includes(seatoolStatus), leadAnalystName, - authority: getAuthority(data.STATE_PLAN?.PLAN_TYPE) as Authority | null, + authority, types: data.STATE_PLAN_SERVICETYPES?.filter( (type): type is NonNullable => type != null, @@ -179,19 +167,10 @@ export const transform = (id: string) => { cmsStatus: cmsStatus || SEATOOL_STATUS.UNKNOWN, seatoolStatus, locked: false, - submissionDate: getDateStringOrNullFromEpoc( - data.STATE_PLAN.SUBMISSION_DATE, - ), + submissionDate: getDateStringOrNullFromEpoc(data.STATE_PLAN.SUBMISSION_DATE), subject: data.STATE_PLAN.TITLE_NAME, - secondClock: isInSecondClock( - raiReceivedDate, - raiWithdrawnDate, - seatoolStatus, - getAuthority(data.STATE_PLAN.PLAN_TYPE), - ), - raiWithdrawEnabled: finalDispositionStatuses.includes(seatoolStatus) - ? false - : undefined, + secondClock: isInSecondClock(raiReceivedDate, raiWithdrawnDate, seatoolStatus, authority), + raiWithdrawEnabled: finalDispositionStatuses.includes(seatoolStatus) ? false : undefined, }; return resp; }); diff --git a/lib/packages/shared-types/opensearch/main/transforms/temporary-extension.ts b/lib/packages/shared-types/opensearch/main/transforms/temporary-extension.ts index c00403dbe4..d252337301 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/temporary-extension.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/temporary-extension.ts @@ -7,27 +7,27 @@ import { export const transform = () => { // any adhoc logic return events["temporary-extension"].schema.transform((data) => { - const timestampDate = new Date(data.timestamp); + const timestampDate = data.timestamp ? new Date(data.timestamp) : undefined; const todayEpoch = seaToolFriendlyTimestamp(timestampDate); const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate); return { additionalInformation: data.additionalInformation, authority: data.authority, - changedDate: new Date(data.timestamp).toISOString(), + changedDate: timestampDate?.toISOString() || null, cmsStatus: "Requested", description: null, id: data.id, - makoChangedDate: new Date(data.timestamp).toISOString(), + makoChangedDate: timestampDate?.toISOString() || null, origin: "OneMAC", originalWaiverNumber: data.waiverNumber, raiWithdrawEnabled: false, // Set to false for new submissions seatoolStatus: SEATOOL_STATUS.PENDING, - state: data.id.split("-")[0], + state: data.id?.split("-")?.[0], stateStatus: "Submitted", - statusDate: new Date(todayEpoch).toISOString(), + statusDate: new Date(todayEpoch).toISOString() || null, subject: null, - submissionDate: new Date(nextBusinessDayEpoch).toISOString(), + submissionDate: new Date(nextBusinessDayEpoch).toISOString() || null, submitterEmail: data.submitterEmail, submitterName: data.submitterName, actionType: data.actionType, diff --git a/lib/packages/shared-types/opensearch/main/transforms/toggle-withdraw-rai.ts b/lib/packages/shared-types/opensearch/main/transforms/toggle-withdraw-rai.ts index 64972726aa..fb83eff5d6 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/toggle-withdraw-rai.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/toggle-withdraw-rai.ts @@ -6,9 +6,7 @@ export const transform = () => { return events["toggle-withdraw-rai"].schema.transform((data) => ({ id: data.id, raiWithdrawEnabled: data.raiWithdrawEnabled, - makoChangedDate: data.timestamp - ? new Date(data.timestamp).toISOString() - : null, + makoChangedDate: data.timestamp ? new Date(data.timestamp).toISOString() : null, })); }; diff --git a/lib/packages/shared-types/opensearch/main/transforms/upload-subsequent-documents.ts b/lib/packages/shared-types/opensearch/main/transforms/upload-subsequent-documents.ts index a9c4c0227f..d81dd8c703 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/upload-subsequent-documents.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/upload-subsequent-documents.ts @@ -3,9 +3,7 @@ import { events } from "../../../events"; export const transform = () => { return events["upload-subsequent-documents"].schema.transform((data) => ({ id: data.id, - makoChangedDate: data.timestamp - ? new Date(data.timestamp).toISOString() - : null, + makoChangedDate: data.timestamp ? new Date(data.timestamp).toISOString() : null, })); }; diff --git a/lib/packages/shared-types/opensearch/main/transforms/withdraw-package.ts b/lib/packages/shared-types/opensearch/main/transforms/withdraw-package.ts index c8e48e4225..6d6ec1d0cf 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/withdraw-package.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/withdraw-package.ts @@ -6,12 +6,10 @@ export const transform = () => { return { id: data.id, raiWithdrawEnabled: false, - makoChangedDate: data.timestamp - ? new Date(data.timestamp).toISOString() - : null, + makoChangedDate: data.timestamp ? new Date(data.timestamp).toISOString() : null, cmsStatus, stateStatus, - finalDispositionDate: new Date(data.timestamp).toISOString(), + finalDispositionDate: data.timestamp ? new Date(data.timestamp).toISOString() : null, seatoolStatus: SEATOOL_STATUS.WITHDRAWN, initialIntakeNeeded: false, locked: true, diff --git a/lib/packages/shared-types/opensearch/main/transforms/withdraw-rai-response.ts b/lib/packages/shared-types/opensearch/main/transforms/withdraw-rai-response.ts index 1c39dac22a..938fd5ebd2 100644 --- a/lib/packages/shared-types/opensearch/main/transforms/withdraw-rai-response.ts +++ b/lib/packages/shared-types/opensearch/main/transforms/withdraw-rai-response.ts @@ -6,12 +6,9 @@ export const transform = () => { return { id: data.id, raiWithdrawEnabled: false, - makoChangedDate: data.timestamp - ? new Date(data.timestamp).toISOString() - : null, + makoChangedDate: data.timestamp ? new Date(data.timestamp).toISOString() : null, cmsStatus, stateStatus, - raiReceivedDate: null, seatoolStatus: SEATOOL_STATUS.PENDING_RAI, locked: true, }; diff --git a/lib/packages/shared-types/seatool-tables/State_Plan.ts b/lib/packages/shared-types/seatool-tables/State_Plan.ts index 4ab7acb568..594f37909f 100644 --- a/lib/packages/shared-types/seatool-tables/State_Plan.ts +++ b/lib/packages/shared-types/seatool-tables/State_Plan.ts @@ -1,7 +1,8 @@ import { z } from "zod"; -export const seaChangedDateSchema = z.object({ +export const seatoolRecordWithUpdatedDateSchema = z.object({ ID_Number: z.string(), Changed_Date: z.number().nullable(), }); -export type SeaStatePlan = z.infer; + +export type SeatoolRecordWithUpdatedDate = z.infer; diff --git a/lib/packages/shared-types/tests/legacy-event.test.ts b/lib/packages/shared-types/tests/legacy-event.test.ts deleted file mode 100644 index 893650f6ed..0000000000 --- a/lib/packages/shared-types/tests/legacy-event.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { it, describe, expect } from "vitest"; -import onemacRecords from "./test-legacy-records.json"; -import { Action, opensearch } from ".."; - -describe("onemac has valid data", () => { - it("has valid data", () => { - for (const record of onemacRecords) { - // Note: This if should be a utility used by sinkChangelog and this test - // The if should be tested independently - if ( - !( - record?.sk !== "Package" && - (record.GSI1pk?.startsWith("OneMAC#submit") || - record.GSI1pk?.startsWith("OneMAC#enable")) - ) - ) { - continue; - } - const transformedData = opensearch.changelog.legacyEvent - .transform("randomid", 12345) - .parse(record); - expect(transformedData).toHaveProperty("id"); - expect(transformedData).toHaveProperty("packageId"); - expect(transformedData).toHaveProperty("timestamp"); - expect(transformedData).toHaveProperty("actionType"); - expect( - [...Object.values(Action), "new-submission"].includes( - transformedData.actionType, - ), - ).toBe(true); - } - }); -}); diff --git a/lib/packages/shared-types/tests/legacy-package-view.test.ts b/lib/packages/shared-types/tests/legacy-package-view.test.ts deleted file mode 100644 index ff86338f38..0000000000 --- a/lib/packages/shared-types/tests/legacy-package-view.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { it, describe, expect } from "vitest"; -import onemacRecords from "./test-legacy-records.json"; -import { opensearch } from ".."; - -describe("onemac has valid data", () => { - it("has valid data", () => { - for (const record of onemacRecords) { - // Note: This if should be a utility used by sinkMain and this test - // The if should be tested independently - if (record.sk !== "Package" || record.submitterName === "-- --") continue; - const transformedData = opensearch.main.legacyPackageView - .transform("randomid") - .parse(record); - expect(transformedData).toHaveProperty("id"); - expect(transformedData).toHaveProperty("submitterName"); - expect(transformedData).toHaveProperty("submitterEmail"); - expect(transformedData).toHaveProperty("origin"); - } - }); -}); diff --git a/lib/packages/shared-types/tests/seatool.test.ts b/lib/packages/shared-types/tests/seatool.test.ts deleted file mode 100644 index f695bfcd89..0000000000 --- a/lib/packages/shared-types/tests/seatool.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { describe, expect, it } from "vitest"; -import seaToolRecords from "./test-seatool.json"; -import { seatoolSchema, opensearch } from "shared-types"; - -describe("seatool has valid data", () => { - it("can be validated against schema", () => { - const parsedRecord = seatoolSchema.parse(seaToolRecords[0]); - expect(parsedRecord.STATE_PLAN.PLAN_TYPE).toBeDefined(); - }); - - it("can be transformed into a new object", () => { - for (const record of seaToolRecords) { - const transformedRecord = opensearch.main.seatool - .transform("randomid") - .parse(record); - expect(transformedRecord.id).toEqual("randomid"); - } - }); -}); diff --git a/lib/packages/shared-types/tests/test-legacy-records.json b/lib/packages/shared-types/tests/test-legacy-records.json deleted file mode 100644 index 063efc888c..0000000000 --- a/lib/packages/shared-types/tests/test-legacy-records.json +++ /dev/null @@ -1,3338 +0,0 @@ -[ - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1634926411688/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634926411688/15MB.pdf" - }, - { - "s3Key": "1634926411689/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634926411689/adobe.pdf" - } - ], - "componentId": "MD-10-2686", - "currentStatus": "Under Review", - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1634926418704, - "clockEndTimestamp": 1642706018704, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-10-2686", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-10-2686", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1638545563506/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638545563506/15MB.pdf" - } - ], - "componentId": "MD.32240", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973218923, - "submissionTimestamp": 1638545567401, - "clockEndTimestamp": 1646321567401, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.32240", - "sk": "OneMAC#1638545567401", - "pk": "MD.32240", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1638545567401, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "finalDispositionDate": "2012-10-03", - "componentType": "waiverappk", - "componentId": "ME-0276.R03.02", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX P3- Maine request to amend HCB", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2012-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "ME-0276.R03.02", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1638545563506/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638545563506/15MB.pdf" - } - ], - "componentId": "MD.32240", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Under Review", - "waiverExtensions": [], - "submissionTimestamp": 1638545567401, - "clockEndTimestamp": 1646321567401, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.32240", - "lastEventTimestamp": 1657824888790, - "raiResponses": [], - "sk": "Package", - "pk": "MD.32240", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1636128529009/adobe.pdf", - "filename": "adobe.pdf", - "title": "Current State Plan", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128529009/adobe.pdf" - }, - { - "s3Key": "1636128529009/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128529009/adobe.pdf" - }, - { - "s3Key": "1636128529009/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128529009/adobe.pdf" - } - ], - "componentId": "MD-67-5524", - "currentStatus": "Submitted", - "convertTimestamp": 1673973226590, - "submissionTimestamp": 1636128530350, - "clockEndTimestamp": 1643908130350, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipspa", - "GSI1sk": "MD-67-5524", - "sk": "OneMAC#1636128530350", - "pk": "MD-67-5524", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1636128530350, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "finalDispositionDate": "2008-06-27", - "componentType": "waiverappk", - "componentId": "MA-0064.92.R4.01", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Massachusetts submitted an ame", - "description": null, - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2007-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "approvedEffectiveDate": "2007-07-01", - "raiResponses": [], - "sk": "Package", - "pk": "MA-0064.92.R4.01", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1636128529009/adobe.pdf", - "filename": "adobe.pdf", - "title": "Current State Plan", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128529009/adobe.pdf" - }, - { - "s3Key": "1636128529009/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128529009/adobe.pdf" - }, - { - "s3Key": "1636128529009/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128529009/adobe.pdf" - } - ], - "componentId": "MD-67-5524", - "currentStatus": "Submitted", - "latestRaiResponseTimestamp": 1636128545397, - "subject": "-- --", - "description": "-- --", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1636128530350, - "clockEndTimestamp": 1643908130350, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-67-5524", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1636128544436/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128544436/adobe.pdf" - }, - { - "s3Key": "1636128544436/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128544436/adobe.pdf" - } - ], - "submissionTimestamp": 1636128545397 - } - ], - "sk": "Package", - "pk": "MD-67-5524", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipsparai", - "attachments": [ - { - "s3Key": "1636128544436/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128544436/adobe.pdf" - }, - { - "s3Key": "1636128544436/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636128544436/adobe.pdf" - } - ], - "componentId": "MD-67-5524", - "currentStatus": "Submitted", - "convertTimestamp": 1673973227011, - "submissionTimestamp": 1636128545397, - "clockEndTimestamp": 1643908145397, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipsparai", - "GSI1sk": "MD-67-5524", - "sk": "OneMAC#1636128545397", - "pk": "MD-67-5524", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1636128545397, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "finalDispositionDate": "2013-11-06", - "componentType": "medicaidspa", - "componentId": "LA-13-32", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Attachment 4.19-C Reimbursemen", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [{ "name": "Billy Bob Farrell", "email": "billy@bob.com" }], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2013-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "LA-13-32", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1643317148757/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1643317148757/15MB.pdf" - }, - { - "s3Key": "1643317148758/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1643317148758/adobe.pdf" - } - ], - "componentId": "MD-55-3946", - "currentStatus": "Submitted", - "convertTimestamp": 1673973231583, - "submissionTimestamp": 1643317153829, - "clockEndTimestamp": 1651089553829, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-55-3946", - "sk": "OneMAC#1643317153829", - "pk": "MD-55-3946", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1643317153829, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1643317148757/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1643317148757/15MB.pdf" - }, - { - "s3Key": "1643317148758/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1643317148758/adobe.pdf" - } - ], - "componentId": "MD-55-3946", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1643317153829, - "clockEndTimestamp": 1651089553829, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-55-3946", - "lastEventTimestamp": 1643317153829, - "raiResponses": [], - "sk": "Package", - "pk": "MD-55-3946", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "chipspa", - "componentId": "PR-18-9011-06a", - "currentStatus": "Under Review", - "subject": "FMG Reports 34a", - "description": "Test Case 34a", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "PR-18-9011-06a", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "componentType": "waivernew", - "componentId": "DC-2019-0608", - "currentStatus": "Pending - Concurrence", - "subject": "Pending Approval 608", - "description": "Testing", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "DC-2019-0608", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "finalDispositionDate": "2012-10-09", - "componentType": "medicaidspa", - "componentId": "CO-12-018", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Remove the entirety of section", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2012-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "approvedEffectiveDate": "2012-07-01", - "raiResponses": [], - "sk": "Package", - "pk": "CO-12-018", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1638387880392/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638387880392/15MB.pdf" - }, - { - "s3Key": "1638387880393/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638387880393/adobe.pdf" - } - ], - "componentId": "MD-23-5964", - "currentStatus": "Under Review", - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1638387886564, - "clockEndTimestamp": 1646163886564, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-23-5964", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-23-5964", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1638387880392/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638387880392/15MB.pdf" - }, - { - "s3Key": "1638387880393/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638387880393/adobe.pdf" - } - ], - "componentId": "MD-23-5964", - "currentStatus": "Submitted", - "convertTimestamp": 1673973215806, - "submissionTimestamp": 1638387886564, - "clockEndTimestamp": 1646163886564, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-23-5964", - "sk": "OneMAC#1638387886564", - "pk": "MD-23-5964", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1638387886564, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "finalDispositionDate": "2007-11-27", - "componentType": "waiverappk", - "componentId": "KY-0333.90.R1.01", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Request to: (1) add goods and", - "description": null, - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2007-10-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "approvedEffectiveDate": "2007-10-01", - "raiResponses": [], - "sk": "Package", - "pk": "KY-0333.90.R1.01", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1638993068758/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638993068758/15MB.pdf" - } - ], - "componentId": "MD.86880", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973218072, - "submissionTimestamp": 1638993072995, - "clockEndTimestamp": 1646769072995, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.86880", - "sk": "OneMAC#1638993072995", - "pk": "MD.86880", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1638993072995, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1638293285464/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638293285464/15MB.pdf" - }, - { - "s3Key": "1638293285464/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638293285464/adobe.pdf" - } - ], - "componentId": "MD-42-5682", - "currentStatus": "Submitted", - "convertTimestamp": 1673973229902, - "submissionTimestamp": 1638293291696, - "clockEndTimestamp": 1646069291696, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-42-5682", - "sk": "OneMAC#1638293291696", - "pk": "MD-42-5682", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1638293291696, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1638993068758/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638993068758/15MB.pdf" - } - ], - "componentId": "MD.86880", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Under Review", - "waiverExtensions": [], - "submissionTimestamp": 1638993072995, - "clockEndTimestamp": 1646769072995, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.86880", - "lastEventTimestamp": 1657824888790, - "raiResponses": [], - "sk": "Package", - "pk": "MD.86880", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1638293285464/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638293285464/15MB.pdf" - }, - { - "s3Key": "1638293285464/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638293285464/adobe.pdf" - } - ], - "componentId": "MD-42-5682", - "currentStatus": "Under Review", - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1638293291696, - "clockEndTimestamp": 1646069291696, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-42-5682", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-42-5682", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1639603061544/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1639603061544/15MB.pdf" - } - ], - "componentId": "MD.62290", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973222344, - "submissionTimestamp": 1639603067763, - "clockEndTimestamp": 1647375467763, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.62290", - "sk": "OneMAC#1639603067763", - "pk": "MD.62290", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1639603067763, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waiverrai", - "attachments": [ - { - "s3Key": "1639603089679/adobe.pdf", - "filename": "adobe.pdf", - "title": "Waiver RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1639603089679/adobe.pdf" - } - ], - "componentId": "MD.62290", - "currentStatus": "Submitted", - "convertTimestamp": 1673973223932, - "parentType": "waiverrenewal", - "submissionTimestamp": 1639603091277, - "clockEndTimestamp": 1647375491277, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaiverrai", - "GSI1sk": "MD.62290", - "sk": "OneMAC#1639603091277", - "pk": "MD.62290", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1639603091277, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1636721631293/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636721631293/15MB.pdf" - }, - { - "s3Key": "1636721631295/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636721631295/adobe.pdf" - } - ], - "componentId": "MD-81-1759", - "currentStatus": "Submitted", - "convertTimestamp": 1673973226523, - "submissionTimestamp": 1636721661672, - "clockEndTimestamp": 1644497661672, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-81-1759", - "sk": "OneMAC#1636721661672", - "pk": "MD-81-1759", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1636721661672, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waiverrenewal", - "attachments": [ - { - "s3Key": "1639603061544/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1639603061544/15MB.pdf" - } - ], - "componentId": "MD.62290", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1639603067763, - "clockEndTimestamp": 1647375467763, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.62290", - "lastEventTimestamp": 1639603091277, - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1639603089679/adobe.pdf", - "filename": "adobe.pdf", - "title": "Waiver RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1639603089679/adobe.pdf" - } - ], - "submissionTimestamp": 1639603091277 - } - ], - "sk": "Package", - "pk": "MD.62290", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1636721631293/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636721631293/15MB.pdf" - }, - { - "s3Key": "1636721631295/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636721631295/adobe.pdf" - } - ], - "componentId": "MD-81-1759", - "currentStatus": "Submitted", - "subject": "-- --", - "description": "-- --", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1636721661672, - "clockEndTimestamp": 1644497661672, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-81-1759", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-81-1759", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1646146433374/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146433374/picture.jpg" - }, - { - "s3Key": "1646146433374/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146433374/adobe.pdf" - }, - { - "s3Key": "1646146433374/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146433374/adobe.pdf" - } - ], - "componentId": "MD-34-4852", - "currentStatus": "Submitted", - "convertTimestamp": 1673973221889, - "submissionTimestamp": 1646146435332, - "clockEndTimestamp": 1653918835332, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipspa", - "GSI1sk": "MD-34-4852", - "sk": "OneMAC#1646146435332", - "pk": "MD-34-4852", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1646146435332, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1638364302646/textnotes.txt", - "filename": "textnotes.txt", - "title": "CMS Form 179", - "contentType": "text/plain", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302646/textnotes.txt" - }, - { - "s3Key": "1638364302646/15MB.pdf", - "filename": "15MB.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302646/15MB.pdf" - }, - { - "s3Key": "1638364302647/textnotes.txt", - "filename": "textnotes.txt", - "title": "SPA Pages", - "contentType": "text/plain", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302647/textnotes.txt" - }, - { - "s3Key": "1638364302647/picture.jpg", - "filename": "picture.jpg", - "title": "SPA Pages", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302647/picture.jpg" - }, - { - "s3Key": "1638364302647/test3.docx", - "filename": "test3.docx", - "title": "SPA Pages", - "contentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302647/test3.docx" - } - ], - "componentId": "MD-57-8134", - "currentStatus": "Submitted", - "convertTimestamp": 1673973218623, - "submissionTimestamp": 1638364307669, - "clockEndTimestamp": 1646140307669, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-57-8134", - "sk": "OneMAC#1638364307669", - "pk": "MD-57-8134", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1638364307669, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipsparai", - "attachments": [ - { - "s3Key": "1646146461022/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146461022/adobe.pdf" - }, - { - "s3Key": "1646146461022/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146461022/adobe.pdf" - } - ], - "componentId": "MD-34-4852", - "currentStatus": "Submitted", - "convertTimestamp": 1673973223649, - "submissionTimestamp": 1646146462194, - "clockEndTimestamp": 1653918862194, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipsparai", - "GSI1sk": "MD-34-4852", - "sk": "OneMAC#1646146462194", - "pk": "MD-34-4852", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1646146462194, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidsparai", - "attachments": [ - { - "s3Key": "1645043182784/adobe.pdf", - "filename": "adobe.pdf", - "title": "RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1645043182784/adobe.pdf" - } - ], - "componentId": "MD-21-7381", - "currentStatus": "Submitted", - "convertTimestamp": 1673973227144, - "submissionTimestamp": 1645043183677, - "clockEndTimestamp": 1652815583677, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidsparai", - "GSI1sk": "MD-21-7381", - "sk": "OneMAC#1645043183677", - "pk": "MD-21-7381", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1645043183677, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1645043130735/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1645043130735/15MB.pdf" - }, - { - "s3Key": "1645043130736/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1645043130736/adobe.pdf" - } - ], - "componentId": "MD-21-7381", - "currentStatus": "Submitted", - "convertTimestamp": 1673973224970, - "submissionTimestamp": 1645043138085, - "clockEndTimestamp": 1652815538085, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-21-7381", - "sk": "OneMAC#1645043138085", - "pk": "MD-21-7381", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1645043138085, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "OK-12-10", - "currentStatus": "-- --", - "subject": "-- --", - "description": "-- --", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "OK-12-10", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1646146433374/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146433374/picture.jpg" - }, - { - "s3Key": "1646146433374/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146433374/adobe.pdf" - }, - { - "s3Key": "1646146433374/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146433374/adobe.pdf" - } - ], - "componentId": "MD-34-4852", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1646146435332, - "clockEndTimestamp": 1653918835332, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-34-4852", - "lastEventTimestamp": 1646146462194, - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1646146461022/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146461022/adobe.pdf" - }, - { - "s3Key": "1646146461022/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1646146461022/adobe.pdf" - } - ], - "submissionTimestamp": 1646146462194 - } - ], - "sk": "Package", - "pk": "MD-34-4852", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1637608173361/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1637608173361/15MB.pdf" - }, - { - "s3Key": "1637608173362/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1637608173362/adobe.pdf" - } - ], - "componentId": "MD-83-5322", - "currentStatus": "Submitted", - "subject": "-- --", - "description": "-- --", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1637608179778, - "clockEndTimestamp": 1645384179778, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-83-5322", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-83-5322", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1638364302646/textnotes.txt", - "filename": "textnotes.txt", - "title": "CMS Form 179", - "contentType": "text/plain", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302646/textnotes.txt" - }, - { - "s3Key": "1638364302646/15MB.pdf", - "filename": "15MB.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302646/15MB.pdf" - }, - { - "s3Key": "1638364302647/textnotes.txt", - "filename": "textnotes.txt", - "title": "SPA Pages", - "contentType": "text/plain", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302647/textnotes.txt" - }, - { - "s3Key": "1638364302647/picture.jpg", - "filename": "picture.jpg", - "title": "SPA Pages", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302647/picture.jpg" - }, - { - "s3Key": "1638364302647/test3.docx", - "filename": "test3.docx", - "title": "SPA Pages", - "contentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638364302647/test3.docx" - } - ], - "componentId": "MD-57-8134", - "currentStatus": "Under Review", - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1638364307669, - "clockEndTimestamp": 1646140307669, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-57-8134", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-57-8134", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1645043130735/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1645043130735/15MB.pdf" - }, - { - "s3Key": "1645043130736/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1645043130736/adobe.pdf" - } - ], - "componentId": "MD-21-7381", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1645043138085, - "clockEndTimestamp": 1652815538085, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-21-7381", - "lastEventTimestamp": 1645043183677, - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1645043182784/adobe.pdf", - "filename": "adobe.pdf", - "title": "RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1645043182784/adobe.pdf" - } - ], - "submissionTimestamp": 1645043183677 - } - ], - "sk": "Package", - "pk": "MD-21-7381", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1637608173361/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1637608173361/15MB.pdf" - }, - { - "s3Key": "1637608173362/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1637608173362/adobe.pdf" - } - ], - "componentId": "MD-83-5322", - "currentStatus": "Submitted", - "convertTimestamp": 1673973227563, - "submissionTimestamp": 1637608179778, - "clockEndTimestamp": 1645384179778, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-83-5322", - "sk": "OneMAC#1637608179778", - "pk": "MD-83-5322", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1637608179778, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "ME-dev-123123", - "currentStatus": "Under Review", - "subject": null, - "description": "", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "ME-dev-123123", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1648566865989/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1648566865989/15MB.pdf" - } - ], - "componentId": "MD.81150", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973231047, - "submissionTimestamp": 1648566869800, - "clockEndTimestamp": 1656342869800, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.81150", - "sk": "OneMAC#1648566869800", - "pk": "MD.81150", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1648566869800, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1648566865989/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1648566865989/15MB.pdf" - } - ], - "componentId": "MD.81150", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1648566869800, - "clockEndTimestamp": 1656342869800, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.81150", - "lastEventTimestamp": 1648566869800, - "raiResponses": [], - "sk": "Package", - "pk": "MD.81150", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "MO-12-15", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Increase the personal needs al", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2012-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MO-12-15", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1641393707058/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641393707058/15MB.pdf" - }, - { - "s3Key": "1641393707058/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641393707058/adobe.pdf" - } - ], - "componentId": "MD-78-5651", - "currentStatus": "Submitted", - "convertTimestamp": 1673973226222, - "submissionTimestamp": 1641393713123, - "clockEndTimestamp": 1649166113123, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-78-5651", - "sk": "OneMAC#1641393713123", - "pk": "MD-78-5651", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1641393713123, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "VA-09-19", - "currentStatus": "-- --", - "subject": "-- --", - "description": "-- --", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "VA-09-19", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1641393707058/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641393707058/15MB.pdf" - }, - { - "s3Key": "1641393707058/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641393707058/adobe.pdf" - } - ], - "componentId": "MD-78-5651", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1641393713123, - "clockEndTimestamp": 1649166113123, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-78-5651", - "lastEventTimestamp": 1641393737633, - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1641393736025/adobe.pdf", - "filename": "adobe.pdf", - "title": "RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641393736025/adobe.pdf" - } - ], - "submissionTimestamp": 1641393737633 - } - ], - "sk": "Package", - "pk": "MD-78-5651", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidsparai", - "attachments": [ - { - "s3Key": "1641393736025/adobe.pdf", - "filename": "adobe.pdf", - "title": "RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641393736025/adobe.pdf" - } - ], - "componentId": "MD-78-5651", - "currentStatus": "Submitted", - "convertTimestamp": 1673973226829, - "submissionTimestamp": 1641393737633, - "clockEndTimestamp": 1649166137633, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidsparai", - "GSI1sk": "MD-78-5651", - "sk": "OneMAC#1641393737633", - "pk": "MD-78-5651", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1641393737633, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "MD-51-5937", - "currentStatus": "Under Review", - "subject": null, - "description": "This is a test, test, test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-51-5937", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "componentType": "medicaidspa", - "componentId": "TX-26-0036", - "currentStatus": "Under Review", - "subject": "test", - "description": "test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1681402639487, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "TX-26-0036", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "componentType": "waiveramendment", - "componentId": "MI-11.R06.M01", - "currentStatus": "-- --", - "subject": "-- --", - "description": "-- --", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MI-11.R06.M01", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "componentType": "medicaidspa", - "componentId": "VA-21-3480", - "currentStatus": "Under Review", - "subject": null, - "description": "", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "VA-21-3480", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1643064903774/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1643064903774/15MB.pdf" - } - ], - "componentId": "MD.38880", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1643064910096, - "clockEndTimestamp": 1650837310096, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.38880", - "lastEventTimestamp": 1643064910096, - "raiResponses": [], - "sk": "Package", - "pk": "MD.38880", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "SC-14-0005", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Addition of adult preventative", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [ - { "name": "Frances Crystal", "email": "frances@crystal.com" } - ], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2014-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "SC-14-0005", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "finalDispositionDate": "2014-07-22", - "componentType": "chipspa", - "componentId": "WY-13-0008-MC2", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX WY CHIP MAGI XXI Medicaid Expa", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [ - { "name": "Stacey Green", "email": "stacey@green.com" }, - { "name": "Janice Adams", "email": "janice@adams.com" } - ], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2014-01-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "approvedEffectiveDate": "2014-01-01", - "raiResponses": [], - "sk": "Package", - "pk": "WY-13-0008-MC2", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1643064903774/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1643064903774/15MB.pdf" - } - ], - "componentId": "MD.38880", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973226847, - "submissionTimestamp": 1643064910096, - "clockEndTimestamp": 1650837310096, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.38880", - "sk": "OneMAC#1643064910096", - "pk": "MD.38880", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1643064910096, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "finalDispositionDate": "2013-03-11", - "componentType": "medicaidspa", - "componentId": "FL-12-012", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Federally Qualified Health Cen", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [ - { "name": "Frances Crystal", "email": "frances@crystal.com" }, - { "name": "Marguerite Schervish", "email": "marguerite@schervish.com" }, - { "name": "Sidney Staton", "email": "sidney@staton.com" } - ], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2012-12-06", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "approvedEffectiveDate": "2012-12-06", - "raiResponses": [], - "sk": "Package", - "pk": "FL-12-012", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1641224502981/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641224502981/15MB.pdf" - }, - { - "s3Key": "1641224502981/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641224502981/adobe.pdf" - } - ], - "componentId": "MD-14-3229", - "currentStatus": "Submitted", - "convertTimestamp": 1673973226583, - "submissionTimestamp": 1641224508544, - "clockEndTimestamp": 1648996908544, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-14-3229", - "sk": "OneMAC#1641224508544", - "pk": "MD-14-3229", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1641224508544, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "finalDispositionDate": "2014-12-17", - "componentType": "medicaidspa", - "componentId": "CO-14-0045", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Govt Operated Providers for Co", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2014-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "approvedEffectiveDate": "2014-07-01", - "raiResponses": [], - "sk": "Package", - "pk": "CO-14-0045", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1641224502981/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641224502981/15MB.pdf" - }, - { - "s3Key": "1641224502981/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1641224502981/adobe.pdf" - } - ], - "componentId": "MD-14-3229", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1641224508544, - "clockEndTimestamp": 1648996908544, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-14-3229", - "lastEventTimestamp": 1641224508544, - "raiResponses": [], - "sk": "Package", - "pk": "MD-14-3229", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "MD-60-5619", - "currentStatus": "Under Review", - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-60-5619", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "finalDispositionDate": "2010-01-15", - "componentType": "medicaidspa", - "componentId": "KS-09-03", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Methods and standards for esta", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2009-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "approvedEffectiveDate": "2009-07-01", - "raiResponses": [], - "sk": "Package", - "pk": "KS-09-03", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "finalDispositionDate": "2014-06-30", - "componentType": "waiverappk", - "componentId": "NJ-0032.R05.01", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Global Options for Long-Term C", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2014-06-30", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "NJ-0032.R05.01", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "Relax. This is only a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1613506484023/file0.txt", - "filename": "file0.txt", - "title": "CMS Form 179", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file0.txt" - }, - { - "s3Key": "1613506484023/file1.txt", - "filename": "file1.txt", - "title": "SPA Pages", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file1.txt" - }, - { - "s3Key": "1613506484023/file10.txt", - "filename": "file10.txt", - "title": "Cover Letter", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file10.txt" - }, - { - "s3Key": "1613506484023/file2.txt", - "filename": "file2.txt", - "title": "Existing State Plan Page(s)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file2.txt" - }, - { - "s3Key": "1613506484024/file3.txt", - "filename": "file3.txt", - "title": "Document Demonstrating Good-Faith Tribal Engagement", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file3.txt" - }, - { - "s3Key": "1613506484024/file4.txt", - "filename": "file4.txt", - "title": "Tribal Consultation", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file4.txt" - }, - { - "s3Key": "1613506484024/file5.txt", - "filename": "file5.txt", - "title": "Public Notice", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file5.txt" - }, - { - "s3Key": "1613506484024/file6.txt", - "filename": "file6.txt", - "title": "Standard Funding Questions (SFQs)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file6.txt" - }, - { - "s3Key": "1613506484024/file7.txt", - "filename": "file7.txt", - "title": "Other", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file7.txt" - } - ], - "componentId": "VA-21-3762-6068", - "currentStatus": "Submitted", - "convertTimestamp": 1673973212246, - "submissionTimestamp": 1613506484813, - "clockEndTimestamp": 1621278884813, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "VA-21-3762-6068", - "sk": "OneMAC#1613506484813", - "pk": "VA-21-3762-6068", - "submitterName": "StateSubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1613506484813, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "Relax. This is only a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1613506484023/file0.txt", - "filename": "file0.txt", - "title": "CMS Form 179", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file0.txt" - }, - { - "s3Key": "1613506484023/file1.txt", - "filename": "file1.txt", - "title": "SPA Pages", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file1.txt" - }, - { - "s3Key": "1613506484023/file10.txt", - "filename": "file10.txt", - "title": "Cover Letter", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file10.txt" - }, - { - "s3Key": "1613506484023/file2.txt", - "filename": "file2.txt", - "title": "Existing State Plan Page(s)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484023/file2.txt" - }, - { - "s3Key": "1613506484024/file3.txt", - "filename": "file3.txt", - "title": "Document Demonstrating Good-Faith Tribal Engagement", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file3.txt" - }, - { - "s3Key": "1613506484024/file4.txt", - "filename": "file4.txt", - "title": "Tribal Consultation", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file4.txt" - }, - { - "s3Key": "1613506484024/file5.txt", - "filename": "file5.txt", - "title": "Public Notice", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file5.txt" - }, - { - "s3Key": "1613506484024/file6.txt", - "filename": "file6.txt", - "title": "Standard Funding Questions (SFQs)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file6.txt" - }, - { - "s3Key": "1613506484024/file7.txt", - "filename": "file7.txt", - "title": "Other", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A2ba92514-961b-4955-8954-3840bdb84b4e/1613506484024/file7.txt" - } - ], - "componentId": "VA-21-3762-6068", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1613506484813, - "clockEndTimestamp": 1621278884813, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "VA-21-3762-6068", - "lastEventTimestamp": 1613506484813, - "raiResponses": [], - "sk": "Package", - "pk": "VA-21-3762-6068", - "submitterName": "StateSubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "MD-36-4631-A", - "currentStatus": "Under Review", - "subject": null, - "description": "This is a test, test, test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-36-4631-A", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1651195078431/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1651195078431/picture.jpg" - }, - { - "s3Key": "1651195078432/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1651195078432/adobe.pdf" - }, - { - "s3Key": "1651195078432/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1651195078432/adobe.pdf" - } - ], - "componentId": "MD-63-2071", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1651195079994, - "clockEndTimestamp": 1658971079994, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-63-2071", - "lastEventTimestamp": 1651195079994, - "raiResponses": [], - "sk": "Package", - "pk": "MD-63-2071", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is a temporary extension test", - "componentType": "waiverextension", - "attachments": [ - { - "s3Key": "1648564191177/15MB.pdf", - "filename": "15MB.pdf", - "title": "Waiver Extension Request", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1648564191177/15MB.pdf" - } - ], - "componentId": "MD.10220", - "auditArray": [ - "UPDATED 1680287160142: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680270031559: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680205877267: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680115344369: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680010842218: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679613068692: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679611875043: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679609163523: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679582888374: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679578022061: currentStatus changed from \"Submitted\" to \"TE Requested\"" - ], - "currentStatus": "TE Requested", - "convertTimestamp": 1673973227052, - "submissionTimestamp": 1648564195171, - "clockEndTimestamp": 1656340195171, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaiverextension", - "GSI1sk": "MD.10220", - "sk": "OneMAC#1648564195171", - "pk": "MD.10220", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1648564195171, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is a temporary extension test", - "componentType": "waiverextension", - "attachments": [ - { - "s3Key": "1648564191177/15MB.pdf", - "filename": "15MB.pdf", - "title": "Waiver Extension Request", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1648564191177/15MB.pdf" - } - ], - "componentId": "MD.10220", - "currentStatus": "TE Requested", - "waiverExtensions": [], - "submissionTimestamp": 1648564195171, - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.10220", - "lastEventTimestamp": 1648564195171, - "raiResponses": [], - "sk": "Package", - "pk": "MD.10220", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1648563688510/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1648563688510/15MB.pdf" - } - ], - "componentId": "MD.10220", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973224344, - "submissionTimestamp": 1648563695451, - "clockEndTimestamp": 1656339695451, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.10220", - "sk": "OneMAC#1648563695451", - "pk": "MD.10220", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1648563695451, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1651195078431/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1651195078431/picture.jpg" - }, - { - "s3Key": "1651195078432/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1651195078432/adobe.pdf" - }, - { - "s3Key": "1651195078432/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1651195078432/adobe.pdf" - } - ], - "componentId": "MD-63-2071", - "currentStatus": "Submitted", - "convertTimestamp": 1673973218210, - "submissionTimestamp": 1651195079994, - "clockEndTimestamp": 1658971079994, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipspa", - "GSI1sk": "MD-63-2071", - "sk": "OneMAC#1651195079994", - "pk": "MD-63-2071", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1651195079994, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is a test, test, testPRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless ", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1629131394530/file.docx", - "filename": "file.docx", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1629131394530/file.docx" - } - ], - "componentId": "MD.42330", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973217981, - "submissionTimestamp": 1629131395161, - "clockEndTimestamp": 1636910995161, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.42330", - "sk": "OneMAC#1629131395161", - "pk": "MD.42330", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1629131395161, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is a test, test, testPRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless ", - "componentType": "waiverextension", - "attachments": [ - { - "s3Key": "1629131438034/file.docx", - "filename": "file.docx", - "title": "Waiver Extension Request", - "contentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1629131438034/file.docx" - } - ], - "componentId": "MD.42330", - "currentStatus": "TE Requested", - "waiverExtensions": [], - "submissionTimestamp": 1629131438604, - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.42330", - "lastEventTimestamp": 1629131438604, - "raiResponses": [], - "sk": "Package", - "pk": "MD.42330", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is a test, test, testPRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless ", - "componentType": "waiverextension", - "attachments": [ - { - "s3Key": "1629131438034/file.docx", - "filename": "file.docx", - "title": "Waiver Extension Request", - "contentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1629131438034/file.docx" - } - ], - "componentId": "MD.42330", - "auditArray": [ - "UPDATED 1680287160142: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680270031560: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680205877267: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680115344370: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1680010842218: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679613068693: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679611875043: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679609163523: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679582888374: currentStatus changed from \"Submitted\" to \"TE Requested\"", - "UPDATED 1679578022062: currentStatus changed from \"Submitted\" to \"TE Requested\"" - ], - "currentStatus": "TE Requested", - "convertTimestamp": 1673973219205, - "submissionTimestamp": 1629131438604, - "clockEndTimestamp": 1636911038604, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaiverextension", - "GSI1sk": "MD.42330", - "sk": "OneMAC#1629131438604", - "pk": "MD.42330", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1629131438604, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1638800656858/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800656858/picture.jpg" - }, - { - "s3Key": "1638800656859/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800656859/adobe.pdf" - }, - { - "s3Key": "1638800656859/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800656859/adobe.pdf" - } - ], - "componentId": "MD-11-5931", - "currentStatus": "Submitted", - "convertTimestamp": 1673973218050, - "submissionTimestamp": 1638800657831, - "clockEndTimestamp": 1646576657831, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipspa", - "GSI1sk": "MD-11-5931", - "sk": "OneMAC#1638800657831", - "pk": "MD-11-5931", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1638800657831, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipsparai", - "attachments": [ - { - "s3Key": "1638800683655/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800683655/adobe.pdf" - }, - { - "s3Key": "1638800683655/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800683655/adobe.pdf" - } - ], - "componentId": "MD-11-5931", - "currentStatus": "Submitted", - "convertTimestamp": 1673973218810, - "submissionTimestamp": 1638800684457, - "clockEndTimestamp": 1646576684457, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipsparai", - "GSI1sk": "MD-11-5931", - "sk": "OneMAC#1638800684457", - "pk": "MD-11-5931", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1638800684457, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1638800656858/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800656858/picture.jpg" - }, - { - "s3Key": "1638800656859/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800656859/adobe.pdf" - }, - { - "s3Key": "1638800656859/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800656859/adobe.pdf" - } - ], - "componentId": "MD-11-5931", - "currentStatus": "Under Review", - "latestRaiResponseTimestamp": 1638800684457, - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1638800657831, - "clockEndTimestamp": 1646576657831, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-11-5931", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1638800683655/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800683655/adobe.pdf" - }, - { - "s3Key": "1638800683655/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1638800683655/adobe.pdf" - } - ], - "submissionTimestamp": 1638800684457 - } - ], - "sk": "Package", - "pk": "MD-11-5931", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1634588147080/adobe.pdf", - "filename": "adobe.pdf", - "title": "Current State Plan", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634588147080/adobe.pdf" - }, - { - "s3Key": "1634588147082/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634588147082/adobe.pdf" - }, - { - "s3Key": "1634588147082/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634588147082/adobe.pdf" - } - ], - "componentId": "MD-50-6431", - "currentStatus": "Submitted", - "convertTimestamp": 1673973217863, - "submissionTimestamp": 1634588148085, - "clockEndTimestamp": 1642367748085, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipspa", - "GSI1sk": "MD-50-6431", - "sk": "OneMAC#1634588148085", - "pk": "MD-50-6431", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1634588148085, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1634588147080/adobe.pdf", - "filename": "adobe.pdf", - "title": "Current State Plan", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634588147080/adobe.pdf" - }, - { - "s3Key": "1634588147082/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634588147082/adobe.pdf" - }, - { - "s3Key": "1634588147082/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1634588147082/adobe.pdf" - } - ], - "componentId": "MD-50-6431", - "currentStatus": "Submitted", - "subject": "-- --", - "description": "-- --", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1634588148085, - "clockEndTimestamp": 1642367748085, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-50-6431", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-50-6431", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1636127849452/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636127849452/15MB.pdf" - }, - { - "s3Key": "1636127849453/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636127849453/adobe.pdf" - } - ], - "componentId": "MD-30-4114", - "currentStatus": "Submitted", - "convertTimestamp": 1673973214989, - "submissionTimestamp": 1636127856554, - "clockEndTimestamp": 1643907456554, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-30-4114", - "sk": "OneMAC#1636127856554", - "pk": "MD-30-4114", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1636127856554, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1636127849452/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636127849452/15MB.pdf" - }, - { - "s3Key": "1636127849453/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636127849453/adobe.pdf" - } - ], - "componentId": "MD-30-4114", - "currentStatus": "Under Review", - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1636127856554, - "clockEndTimestamp": 1643907456554, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-30-4114", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-30-4114", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "WI-14-0019", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Payment of Nursing Facility an", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2014-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "WI-14-0019", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipsparai", - "attachments": [ - { - "s3Key": "1650575913361/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575913361/adobe.pdf" - }, - { - "s3Key": "1650575913361/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575913361/adobe.pdf" - } - ], - "componentId": "MD-84-5014", - "currentStatus": "Submitted", - "convertTimestamp": 1673973224968, - "submissionTimestamp": 1650575914061, - "clockEndTimestamp": 1658351914061, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipsparai", - "GSI1sk": "MD-84-5014", - "sk": "OneMAC#1650575914061", - "pk": "MD-84-5014", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1650575914061, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1636571876729/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636571876729/15MB.pdf" - }, - { - "s3Key": "1636571876730/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636571876730/adobe.pdf" - } - ], - "componentId": "MD-48-8946", - "currentStatus": "Submitted", - "convertTimestamp": 1673973231305, - "submissionTimestamp": 1636571894157, - "clockEndTimestamp": 1644347894157, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-48-8946", - "sk": "OneMAC#1636571894157", - "pk": "MD-48-8946", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1636571894157, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1650575879113/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575879113/picture.jpg" - }, - { - "s3Key": "1650575879114/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575879114/adobe.pdf" - }, - { - "s3Key": "1650575879114/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575879114/adobe.pdf" - } - ], - "componentId": "MD-84-5014", - "currentStatus": "Submitted", - "convertTimestamp": 1673973224171, - "submissionTimestamp": 1650575880583, - "clockEndTimestamp": 1658351880583, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitchipspa", - "GSI1sk": "MD-84-5014", - "sk": "OneMAC#1650575880583", - "pk": "MD-84-5014", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1650575880583, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1636571876729/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636571876729/15MB.pdf" - }, - { - "s3Key": "1636571876730/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1636571876730/adobe.pdf" - } - ], - "componentId": "MD-48-8946", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1636571894157, - "clockEndTimestamp": 1644347894157, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-48-8946", - "lastEventTimestamp": 1636571894157, - "raiResponses": [], - "sk": "Package", - "pk": "MD-48-8946", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "MD-57-4961", - "currentStatus": "Under Review", - "subject": null, - "description": "This is a test, test, test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-57-4961", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "componentType": "medicaidspa", - "componentId": "MD-21-9999", - "currentStatus": "Under Review", - "subject": null, - "description": "Hello World", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MD-21-9999", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1642622159645/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1642622159645/15MB.pdf" - } - ], - "componentId": "MD.29280", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "convertTimestamp": 1673973229285, - "submissionTimestamp": 1642622164522, - "clockEndTimestamp": 1650394564522, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitwaivernew", - "GSI1sk": "MD.29280", - "sk": "OneMAC#1642622164522", - "pk": "MD.29280", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1642622164522, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "MT-14-0044", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Primary Care Service Payment E", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2015-01-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MT-14-0044", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "finalDispositionDate": "2014-04-08", - "componentType": "waiverappk", - "componentId": "AR-0400.02.04", - "currentStatus": "Package Withdrawn", - "subject": null, - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [{ "name": "Billy Bob Farrell", "email": "billy@bob.com" }], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2014-07-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "AR-0400.02.04", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "chipspa", - "attachments": [ - { - "s3Key": "1650575879113/picture.jpg", - "filename": "picture.jpg", - "title": "Current State Plan", - "contentType": "image/jpeg", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575879113/picture.jpg" - }, - { - "s3Key": "1650575879114/adobe.pdf", - "filename": "adobe.pdf", - "title": "Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575879114/adobe.pdf" - }, - { - "s3Key": "1650575879114/adobe.pdf", - "filename": "adobe.pdf", - "title": "Cover Letter", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575879114/adobe.pdf" - } - ], - "componentId": "MD-84-5014", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1650575880583, - "clockEndTimestamp": 1658351880583, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-84-5014", - "lastEventTimestamp": 1650575914061, - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1650575913361/adobe.pdf", - "filename": "adobe.pdf", - "title": "Revised Amended State Plan Language", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575913361/adobe.pdf" - }, - { - "s3Key": "1650575913361/adobe.pdf", - "filename": "adobe.pdf", - "title": "Official RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1650575913361/adobe.pdf" - } - ], - "submissionTimestamp": 1650575914061 - } - ], - "sk": "Package", - "pk": "MD-84-5014", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "TX-11-59", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX P-1 Physicians & Pratitioners", - "description": null, - "waiverExtensions": [], - "reviewTeam": [ - "Cheryl Rupley", - "John Castro", - "Rene Spencer", - "Karen Prisby" - ], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2011-10-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "TX-11-59", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "waivernew", - "attachments": [ - { - "s3Key": "1642622159645/15MB.pdf", - "filename": "15MB.pdf", - "title": "1915(b)(4) FFS Selective Contracting (Streamlined) waiver application pre-print (Initial, Renewal, Amendment)", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1642622159645/15MB.pdf" - } - ], - "componentId": "MD.29280", - "waiverAuthority": "1915(b)(4)", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1642622164522, - "clockEndTimestamp": 1650394564522, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#waiver", - "GSI1sk": "MD.29280", - "lastEventTimestamp": 1642622164522, - "raiResponses": [], - "sk": "Package", - "pk": "MD.29280", - "submitterName": "Statesubmitter Nightwatch", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "componentType": "medicaidspa", - "componentId": "MI-10-001", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX Remove ICF/MR related language", - "description": null, - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2010-04-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "MI-10-001", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "componentType": "medicaidspa", - "componentId": "PA-14-0012-MM1", - "currentStatus": "Approved", - "subject": "XX_SEA_VAL_XX MAGI Based Eligibility Groups ", - "description": "XX_SEA_VAL_XX This is some text", - "waiverExtensions": [], - "reviewTeam": [{ "name": "Mary Corddry", "email": "mary@corddry.com" }], - "submissionTimestamp": 0, - "proposedEffectiveDate": "2014-01-01", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "PA-14-0012-MM1", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1635352815650/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1635352815650/15MB.pdf" - }, - { - "s3Key": "1635352815651/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1635352815651/adobe.pdf" - } - ], - "componentId": "MD-57-7063", - "currentStatus": "Submitted", - "convertTimestamp": 1673973224684, - "submissionTimestamp": 1635352822914, - "clockEndTimestamp": 1643132422914, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "MD-57-7063", - "sk": "OneMAC#1635352822914", - "pk": "MD-57-7063", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1635352822914, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidsparai", - "attachments": [ - { - "s3Key": "1635352841078/adobe.pdf", - "filename": "adobe.pdf", - "title": "RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1635352841078/adobe.pdf" - } - ], - "componentId": "MD-57-7063", - "currentStatus": "Submitted", - "convertTimestamp": 1673973226230, - "submissionTimestamp": 1635352841908, - "clockEndTimestamp": 1643132441908, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidsparai", - "GSI1sk": "MD-57-7063", - "sk": "OneMAC#1635352841908", - "pk": "MD-57-7063", - "submitterName": "Statesubmitter Nightwatch", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1635352841908, - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "finalDispositionDate": "2018-04-18", - "componentType": "waiverrenewal", - "componentId": "HI-9002", - "currentStatus": "Package Withdrawn", - "subject": "HI-9002", - "description": "required", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "HI-9002", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "This is just a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1635352815650/15MB.pdf", - "filename": "15MB.pdf", - "title": "CMS Form 179", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1635352815650/15MB.pdf" - }, - { - "s3Key": "1635352815651/adobe.pdf", - "filename": "adobe.pdf", - "title": "SPA Pages", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1635352815651/adobe.pdf" - } - ], - "componentId": "MD-57-7063", - "currentStatus": "Under Review", - "latestRaiResponseTimestamp": 1635352841908, - "subject": null, - "description": "This is just a test", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 1635352822914, - "clockEndTimestamp": 1643132422914, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "MD-57-7063", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [ - { - "additionalInformation": "This is just a test", - "attachments": [ - { - "s3Key": "1635352841078/adobe.pdf", - "filename": "adobe.pdf", - "title": "RAI Response", - "contentType": "application/pdf", - "url": "https://uploads-develop-attachments-116229642442.s3.us-east-1.amazonaws.com/protected/us-east-1%3A86a190fe-b195-42bf-9685-9761bf0ff14b/1635352841078/adobe.pdf" - } - ], - "submissionTimestamp": 1635352841908 - } - ], - "sk": "Package", - "pk": "MD-57-7063", - "submitterName": "Statesubmitter Nightwatch", - "cpocName": "-- --", - "submitterEmail": "statesubmitter@nightwatch.test" - }, - { - "additionalInformation": "Relax. This is only a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1612800700066/file0.txt", - "filename": "file0.txt", - "title": "CMS Form 179", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700066/file0.txt" - }, - { - "s3Key": "1612800700107/file1.txt", - "filename": "file1.txt", - "title": "SPA Pages", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file1.txt" - }, - { - "s3Key": "1612800700107/file10.txt", - "filename": "file10.txt", - "title": "Cover Letter", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file10.txt" - }, - { - "s3Key": "1612800700107/file2.txt", - "filename": "file2.txt", - "title": "Existing State Plan Page(s)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file2.txt" - }, - { - "s3Key": "1612800700107/file3.txt", - "filename": "file3.txt", - "title": "Document Demonstrating Good-Faith Tribal Engagement", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file3.txt" - }, - { - "s3Key": "1612800700107/file4.txt", - "filename": "file4.txt", - "title": "Tribal Consultation", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file4.txt" - }, - { - "s3Key": "1612800700107/file5.txt", - "filename": "file5.txt", - "title": "Public Notice", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file5.txt" - }, - { - "s3Key": "1612800700107/file6.txt", - "filename": "file6.txt", - "title": "Standard Funding Questions (SFQs)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file6.txt" - }, - { - "s3Key": "1612800700107/file7.txt", - "filename": "file7.txt", - "title": "Other", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file7.txt" - } - ], - "componentId": "VA-21-1673", - "currentStatus": "Submitted", - "convertTimestamp": 1673973209405, - "submissionTimestamp": 1612800701871, - "clockEndTimestamp": 1620573101871, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#submitmedicaidspa", - "GSI1sk": "VA-21-1673", - "sk": "OneMAC#1612800701871", - "pk": "VA-21-1673", - "submitterName": "Randy Revoked", - "originallyFrom": "cms-spa-form-develop-change-requests", - "eventTimestamp": 1612800701871, - "submitterEmail": "statesubmitterrevoked@cms.hhs.local" - }, - { - "componentType": "chipspa", - "componentId": "PR-18-9014-04A", - "currentStatus": "Pending - Concurrence", - "subject": "FMG Reports 53a", - "description": "Test Case 53a", - "waiverExtensions": [], - "reviewTeam": [], - "submissionTimestamp": 0, - "proposedEffectiveDate": "none", - "lastEventTimestamp": 1657824888790, - "withdrawalRequests": [], - "raiResponses": [], - "sk": "Package", - "pk": "PR-18-9014-04A", - "submitterName": "-- --", - "cpocName": "-- --", - "submitterEmail": "-- --" - }, - { - "additionalInformation": "Relax. This is only a test", - "componentType": "medicaidspa", - "attachments": [ - { - "s3Key": "1612800700066/file0.txt", - "filename": "file0.txt", - "title": "CMS Form 179", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700066/file0.txt" - }, - { - "s3Key": "1612800700107/file1.txt", - "filename": "file1.txt", - "title": "SPA Pages", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file1.txt" - }, - { - "s3Key": "1612800700107/file10.txt", - "filename": "file10.txt", - "title": "Cover Letter", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file10.txt" - }, - { - "s3Key": "1612800700107/file2.txt", - "filename": "file2.txt", - "title": "Existing State Plan Page(s)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file2.txt" - }, - { - "s3Key": "1612800700107/file3.txt", - "filename": "file3.txt", - "title": "Document Demonstrating Good-Faith Tribal Engagement", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file3.txt" - }, - { - "s3Key": "1612800700107/file4.txt", - "filename": "file4.txt", - "title": "Tribal Consultation", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file4.txt" - }, - { - "s3Key": "1612800700107/file5.txt", - "filename": "file5.txt", - "title": "Public Notice", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file5.txt" - }, - { - "s3Key": "1612800700107/file6.txt", - "filename": "file6.txt", - "title": "Standard Funding Questions (SFQs)", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file6.txt" - }, - { - "s3Key": "1612800700107/file7.txt", - "filename": "file7.txt", - "title": "Other", - "contentType": "text/plain", - "url": "https://uploads-develop-attachmentsbucket-10wg5kiraihu1.s3.amazonaws.com/protected/us-east-1%3A4480cb87-b4ab-4556-acef-d91f38901059/1612800700107/file7.txt" - } - ], - "componentId": "VA-21-1673", - "currentStatus": "Submitted", - "waiverExtensions": [], - "submissionTimestamp": 1612800701871, - "clockEndTimestamp": 1620573101871, - "proposedEffectiveDate": "none", - "GSI1pk": "OneMAC#spa", - "GSI1sk": "VA-21-1673", - "lastEventTimestamp": 1612800701871, - "raiResponses": [], - "sk": "Package", - "pk": "VA-21-1673", - "submitterName": "Randy Revoked", - "submitterEmail": "statesubmitterrevoked@cms.hhs.local" - } -] diff --git a/lib/packages/shared-types/tests/test-seatool.json b/lib/packages/shared-types/tests/test-seatool.json deleted file mode 100644 index 82921eb2a3..0000000000 --- a/lib/packages/shared-types/tests/test-seatool.json +++ /dev/null @@ -1,7749 +0,0 @@ -[ - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-4087", - "SUBMISSION_DATE": 1625164258000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625164258000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "81DAEA50-7927-4DB9-A229-0EC7689FBDD7" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-4455", - "SUBMISSION_DATE": 1636137448000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1636137448000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "673D3E71-4484-465A-8210-94C71465C344" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-4575-A", - "SUBMISSION_DATE": 1626140645000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1626140645000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "1AF8C66F-B456-4443-9D44-BC3F194C99DF" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-4651", - "SUBMISSION_DATE": 1624295568000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624295568000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "287838FC-A9AC-4784-8236-C6D9ED73106D" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-4932", - "SUBMISSION_DATE": 1624651979000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624651979000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "8B41A22F-2F16-42F6-B941-26008B730A4C" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-4976", - "SUBMISSION_DATE": 1637951998000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1637951998000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "0E88A1FC-BF01-4E63-9EFA-A53E18E45FA8" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-5005-1297", - "SUBMISSION_DATE": 1624467866000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624467866000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "4C9835E8-5F80-4825-9F58-F710A25C9490" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-5079-A", - "SUBMISSION_DATE": 1625173712000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625173712000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C5DF7819-CBF1-473A-9022-53F98CDACCFA" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-5322", - "SUBMISSION_DATE": 1637608179000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1637608179000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "924F13A4-46EA-40E8-BD03-2D477F3006E2" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-5552", - "SUBMISSION_DATE": 1638471334000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638471334000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "3E235F11-E28F-4359-93DE-F50908A1643F" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-5773", - "SUBMISSION_DATE": 1636137987000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1636137987000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "5339DDFD-FA83-4B51-A63B-A030F09BF3CA" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-5787", - "SUBMISSION_DATE": 1636398066000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1636398066000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "AD519469-A306-42F8-86A5-FADA9E6F7114" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-6146", - "SUBMISSION_DATE": 1637780648000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1637780648000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "8D248ADB-2FCC-4C77-88FF-FA7C4F05585A" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-6271-3344", - "SUBMISSION_DATE": 1626292243000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1626292243000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C1038CA4-95CD-4632-BDF9-15BD5CAE9758" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-6513", - "SUBMISSION_DATE": 1638917844000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638917844000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "3434637A-5B2E-41F0-BC91-7807458488D8" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-6651-2613", - "SUBMISSION_DATE": 1624899476000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624899476000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "B4DB7614-394A-4A48-B511-C3884870D375" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-6776", - "SUBMISSION_DATE": 1638478834000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638478834000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C53C2C5E-BBBF-44CA-AF02-286FF502E56E" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-6795-6972", - "SUBMISSION_DATE": 1624471968000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624471968000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "34A118D4-26B8-4748-9FAC-56D178FB3522" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7218-6799", - "SUBMISSION_DATE": 1626210051000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1626210051000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "7BFC8CC7-D1C5-428E-941E-0CD5799CB179" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7381", - "SUBMISSION_DATE": 1624294730000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624294730000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "8895A5ED-CBC9-4A04-9A4B-C8F9DD757530" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7412", - "SUBMISSION_DATE": 1626113829000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1626113829000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "2FF54B27-41E5-4675-AA7D-821D3F4565F9" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7459", - "SUBMISSION_DATE": 1633637296000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1633637296000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "47185824-462F-49EF-AFC0-E4BADA659F06" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7549-A", - "SUBMISSION_DATE": 1624978145000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624978145000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C484862D-C7F6-450D-B2C6-513D695B5B4A" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7581", - "SUBMISSION_DATE": 1625087021000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625087021000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C273BD45-C10D-4922-9E28-E94CC112C8CB" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7682", - "SUBMISSION_DATE": 1639164123000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1639164123000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "6663AC38-0B4E-4DEA-83B4-4A046CA094A6" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7710-5738", - "SUBMISSION_DATE": 1625245082000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625245082000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C511F68D-F695-4B66-9D7A-467007381EB0" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7716", - "SUBMISSION_DATE": 1638813549000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638813549000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "ABBCCEE6-9337-41E4-BD93-847A0FAE1BB3" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7767-6466", - "SUBMISSION_DATE": 1622180647000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1622180647000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "83358A74-AE5E-4F06-8ECF-856E250C92FF" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7770-1901", - "SUBMISSION_DATE": 1622180851000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1622180851000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "95AA4FEA-7B67-4479-A952-2F3307492D86" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7876", - "SUBMISSION_DATE": 1638974347000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638974347000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "BA117531-B6A9-49BF-AA20-5BD18D337FDB" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7910", - "SUBMISSION_DATE": 1635271412000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1635271412000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "83B470ED-01DE-4FC8-B873-2371FBA3AE04" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7915", - "SUBMISSION_DATE": 1624418923000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624418923000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "978C99CE-4E13-4B5F-B969-F0869C721725" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7956", - "SUBMISSION_DATE": 1625064589000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625064589000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "625B57C2-A681-478F-9902-40EEBC90962F" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7985", - "SUBMISSION_DATE": 1636473517000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1636473517000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "8AEDD7EA-C4B6-470E-B3B3-1034663ABC5D" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-7990-A", - "SUBMISSION_DATE": 1626186533000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1626186533000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "94E1C6CE-F9B8-412D-8101-9EC648E5E8DB" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-8033", - "SUBMISSION_DATE": 1625849663000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625849663000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "9AEC1094-4B07-4334-8219-747E95771215" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-8046", - "SUBMISSION_DATE": 1636746579000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1636746579000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "008C63CB-4CB8-4E28-B790-8BBD0F1C4482" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-8369", - "SUBMISSION_DATE": 1626209615000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, testPRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-1188. The time required to complete this information collection is estimated to average 40 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850. PRA Disclosure Statement: According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless ", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1626209615000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "B1CA8379-DF5A-4AFB-9928-4CF8C1F61005" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-8433", - "SUBMISSION_DATE": 1625167070000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625167070000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "6630F14D-DC99-4428-AAA1-37919D7E209A" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-8439", - "SUBMISSION_DATE": 1637706543000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1637706543000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "27169062-5E55-4640-A03F-3558E8D98C84" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-83-8875", - "SUBMISSION_DATE": 1636144965000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1636144965000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C85948AA-3F60-425C-9DD5-34F5A5AC0737" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-1430", - "SUBMISSION_DATE": 1637352085000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1637352085000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "F3D57F9A-1E2B-408C-A0AD-4923AB24BF9B" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-1469", - "SUBMISSION_DATE": 1638475986000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638475986000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "F62BDB2E-F5A8-4DC0-8237-9331F136330A" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-1839", - "SUBMISSION_DATE": 1625860055000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625860055000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "39B28B8A-A538-4000-9826-2FE2556E310C" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-1951", - "SUBMISSION_DATE": 1624977876000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624977876000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "02CA9F35-78CA-477D-B729-E2F116027DE3" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-2087-3294", - "SUBMISSION_DATE": 1625859814000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625859814000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "2A1DF409-14D3-466A-B8AB-4EFDA300A129" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-2142", - "SUBMISSION_DATE": 1637184425000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1637184425000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "A3AC1886-BC1A-489B-AD46-60DE14B1D995" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-2238", - "SUBMISSION_DATE": 1638472432000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638472432000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "EAF8A7F5-CA44-4919-AB73-170B308F29BD" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-2313", - "SUBMISSION_DATE": 1626117865000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1626117865000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "922AC65C-59E4-4F3E-8C53-D2A876ED9FEB" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-2438-A", - "SUBMISSION_DATE": 1624385784000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624385784000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C9275C4B-D5EF-4D8E-B2F8-98FDACBA225E" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3032-A", - "SUBMISSION_DATE": 1624474682000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624474682000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "9F527CDF-A883-4B5F-BEDB-D3D10545F602" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3147-5620", - "SUBMISSION_DATE": 1625833161000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": null, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625833161000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "93645B79-0AE6-4F51-B155-34F3DCCA42F4" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": null - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3336", - "SUBMISSION_DATE": 1634833788000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1634833788000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "5C154330-58FE-4005-B930-CD69324F88F4" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3369-A", - "SUBMISSION_DATE": 1624392257000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1624392257000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "CB0B0E4B-5DB2-44C3-82D0-95B45E4B215C" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3589", - "SUBMISSION_DATE": 1636401160000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1636401160000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "DEE55876-0E3A-47E7-9A0B-4064B3CBF12D" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3781", - "SUBMISSION_DATE": 1638471556000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638471556000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "090A8D88-4E1B-4123-9B14-C9AB3974E905" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3843", - "SUBMISSION_DATE": 1625666779000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is a test, test, test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1625666779000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "4F2F26EF-E0C9-4B06-8710-2DDFC0DC6097" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-3845", - "SUBMISSION_DATE": 1634583887000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1634583887000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "45E521BC-4C0D-4524-AD93-32CB53789109" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-4032", - "SUBMISSION_DATE": 1638456977000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 125, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638456977000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "123383D5-3B9B-41A8-A715-0CBA0F237961" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 125, "PLAN_TYPE_NAME": "Medicaid SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-4174", - "SUBMISSION_DATE": 1638293139000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638293139000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "C5D766C2-8CC8-4092-9E05-B61E01A03001" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - }, - { - "STATE_PLAN": { - "ID_NUMBER": "MD-84-4920", - "SUBMISSION_DATE": 1638475432000, - "START_CLOCK_DATE": null, - "REGION_ID": "3", - "COMPONENT_ID": null, - "STATE_CODE": "MD", - "PROPOSED_DATE": null, - "PLAN_TYPE": 124, - "ACTION_TYPE": null, - "CO_SUBMISSION_DATE": null, - "LEAD_ANALYST_ID": null, - "APPROVAL_STATUS_TYPE": null, - "APPROVED_EFFECTIVE_DATE": null, - "ACTUAL_EFFECTIVE_DATE": null, - "DAYS_EXTENSION_NUMBER": null, - "TITLE_NAME": null, - "ALERT_90_DAYS_DATE": null, - "ALERT_MILESTONE1_DAYS": null, - "ALERT_MILESTONE2_DAYS": null, - "ALERT_MILESTONE3_DAYS": null, - "ALERT_MILESTONE4_DAYS": null, - "END_DATE": null, - "REMARKS_MEMO": null, - "STATUS_MEMO": null, - "SUMMARY_MEMO": "This is just a test", - "PRIORITY_COMMENTS_MEMO": null, - "BUDGET_NEUTRALITY_ESTABLISHED_FLAG": false, - "BUDGET_NEUTRALITY_STATUS_MEMO": null, - "BUDGET_IMPACT": null, - "BUDGET_IMPACT_VALUE": null, - "CHANGED_DATE": 1657824888790, - "SPA_TYPE_ID": null, - "TYPE_ID": null, - "STATUS_DATE": 1638475432000, - "SPW_STATUS_ID": 1, - "PRIORITY_CODE_ID": null, - "PRIORITY_COMPLEXITY_ID": null, - "REVIEW_POSITION_ID": null, - "FRT_DATE": null, - "CURRENT_WAIVER_TE": null, - "CURRENT_WAIVER_EXPIRES_DATE": null, - "TE_END_DATE": null, - "OCD_REVIEW_ID": null, - "OCD_REVIEW_COMMENTS_MEMO": null, - "COMPANION_LETTER_REQUESTED_DATE": null, - "COMPANION_LETTER_RECEIVED_DATE": null, - "RO_ANALYST_ID": null, - "SPW_IMPORT": null, - "MMDL_IMPORT": false, - "APPROVAL_DOCS_RECEIVED": null, - "BLOCKING_SPAS_MEMO": null, - "CALL_HELD": null, - "CALL_HELD_REASON_ID": null, - "CODE_AFTER_INIT_ASSESS_ID": null, - "DATE_OF_CODING_CHANGE": null, - "INITIAL_SUBMISSION_COMPLETE": null, - "MISSING_INFORMATION": null, - "BACKUP_PROGRAM_ANALYST_ID": null, - "BACKUP_FM_ANALYST_ID": null, - "PENDING_CONCURRENCE_DATE": null, - "GAP": null, - "ATTACHED_SPA": null, - "UPL_ACCEPTED": null, - "TEMPLATE_ISSUES": null, - "TEMPLATE_ISSUES_MEMO": null, - "TEMPLATE_ISSUES_RESOLVED": null, - "GAP2": null, - "GAP3": null, - "GAP_NA": false, - "GAP2_NA": false, - "GAP3_NA": false, - "GUIDANCE_DOCS_SUBMITTED": false, - "PUBLICHEALTH_STATEEMERGENCY": false, - "SUBMISSION_TYPE": null, - "FISCAL_YEAR": null, - "FISCAL_QUARTER": null, - "DATE_SENT_PSCCAS": null, - "ELIMINATED_COST": false, - "ADDED_COST": false, - "ORGANIZATION_CHANGE": false, - "UUID": "E52B5506-D8B6-456C-8C31-ECE9D209456A" - }, - "ACTION_OFFICERS": null, - "COMPONENTS_SP": null, - "SP1115": null, - "SP_APD_SUB_TYPE": null, - "SP_EARLY_ALERTS": null, - "RAI": null, - "SP_APD": null, - "SP_IMPACT_FUNDING": null, - "STOP_RESUME_DATES": null, - "ACTIONTYPES": null, - "CALLHELDREASONS": null, - "CODEAFTERINITACCESS": null, - "COMPONENTS": null, - "OCD_REVIEW": null, - "RO_ANALYST": null, - "PROGRAM_ANALYST": null, - "FM_ANALYST": null, - "LEAD_ANALYST": null, - "PRIORITY_CODES": null, - "PRIORITY_COMPLEXITY": null, - "REVIEW_POSITION": null, - "REGION": [ - { - "REGION_ID": "3", - "REGION_NAME": "Philadelphia", - "ALERTS_INBOX_ADDRESS": "SPA_Waivers_Philadelphia_R03@cms.hhs.gov" - } - ], - "SPA_TYPE": null, - "SPW_STATUS": [{ "SPW_STATUS_ID": 1, "SPW_STATUS_DESC": "Pending" }], - "STATES": [ - { - "STATE_CODE": "MD", - "REGION_ID": "3", - "STATE_NAME": "Maryland", - "PRIORITY_FLAG": false - } - ], - "SP_TYPE": null, - "STATE_PLAN_SERVICETYPES": null, - "STATE_PLAN_SERVICE_SUBTYPES": null, - "PLAN_TYPES": [{ "PLAN_TYPE_ID": 124, "PLAN_TYPE_NAME": "CHIP SPA" }] - } -] diff --git a/lib/packages/shared-types/tsconfig.json b/lib/packages/shared-types/tsconfig.json index 325e797aca..eec6dd5174 100644 --- a/lib/packages/shared-types/tsconfig.json +++ b/lib/packages/shared-types/tsconfig.json @@ -1,12 +1,15 @@ { "compilerOptions": { + "strict": true, + "forceConsistentCasingInFileNames": true, "target": "ES2016", "moduleResolution": "Bundler", "module": "ESNext", "skipLibCheck": true, "esModuleInterop": true, "resolveJsonModule": true, - "verbatimModuleSyntax": false + "verbatimModuleSyntax": false, + "noEmit": true }, "include": ["./**/*.ts"], "exclude": ["node_modules"] diff --git a/lib/packages/shared-types/user.ts b/lib/packages/shared-types/user.ts index 50fe916fff..c66b4d601f 100644 --- a/lib/packages/shared-types/user.ts +++ b/lib/packages/shared-types/user.ts @@ -1,5 +1,13 @@ import { UserStatusType } from "@aws-sdk/client-cognito-identity-provider"; +export { CognitoUser } from "amazon-cognito-identity-js"; +export type { UserData } from "amazon-cognito-identity-js"; +export type { + APIGatewayEvent, + APIGatewayEventIdentity, + APIGatewayEventRequestContext, +} from "aws-lambda"; + export enum UserRoles { CMS_READ_ONLY = "onemac-micro-readonly", CMS_REVIEWER = "onemac-micro-reviewer", diff --git a/lib/packages/shared-utils/cloudformation.test.ts b/lib/packages/shared-utils/cloudformation.test.ts index 9272d7f9ba..e48b3ad604 100644 --- a/lib/packages/shared-utils/cloudformation.test.ts +++ b/lib/packages/shared-utils/cloudformation.test.ts @@ -1,52 +1,23 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; +import { TEST_CF_EXPORT_ID, TEST_CF_EXPORT_NOT_FOUND_ID, errorCloudFormation } from "mocks"; +import { mockedServer } from "mocks/server"; +import { describe, expect, it } from "vitest"; import { getExport } from "./cloudformation"; -const mockSend = vi.fn(); - -vi.mock("@aws-sdk/client-cloudformation", () => { - return { - CloudFormationClient: vi.fn(() => ({ - send: mockSend, - })), - ListExportsCommand: vi.fn(), - }; -}); - describe("getExport", () => { - beforeEach(() => { - mockSend.mockClear(); - }); - it("should return the export value if the export exists", async () => { - const exportName = "test-export"; - const expectedValue = "test-value"; - - mockSend.mockResolvedValueOnce({ - Exports: [{ Name: exportName, Value: expectedValue }], - }); - - const result = await getExport(exportName); - expect(result).toBe(expectedValue); + const result = await getExport(TEST_CF_EXPORT_ID); + expect(result).toBe("test-value"); }); it("should throw an error if the export does not exist", async () => { - const exportName = "non-existent-export"; - - mockSend.mockResolvedValueOnce({ - Exports: [], - }); - - await expect(getExport(exportName)).rejects.toThrow( - `Export with name ${exportName} does not exist.`, + await expect(getExport(TEST_CF_EXPORT_NOT_FOUND_ID)).rejects.toThrow( + `Export with name ${TEST_CF_EXPORT_NOT_FOUND_ID} does not exist.`, ); }); it("should throw an error if there is an issue with the AWS SDK call", async () => { - const exportName = "test-export"; - const errorMessage = "AWS SDK error"; - - mockSend.mockRejectedValueOnce(new Error(errorMessage)); + mockedServer.use(errorCloudFormation); - await expect(getExport(exportName)).rejects.toThrow(errorMessage); + await expect(getExport(TEST_CF_EXPORT_ID)).rejects.toThrow("UnknownError"); }); }); diff --git a/lib/packages/shared-utils/decode.ts b/lib/packages/shared-utils/decode.ts index 56727d2791..0e85f83ca8 100644 --- a/lib/packages/shared-utils/decode.ts +++ b/lib/packages/shared-utils/decode.ts @@ -1,7 +1,5 @@ -export function decodeBase64WithUtf8(value: string) { - // Assuming the record value is in base64 encoding - const base64String = value; - const buffer = Buffer.from(base64String, "base64"); - const decodedString = buffer.toString("utf-8"); - return decodedString; +export function decodeBase64WithUtf8(base64Value: string) { + const buffer = Buffer.from(base64Value, "base64"); + + return buffer.toString("utf-8"); } diff --git a/lib/packages/shared-utils/package-actions/getAvailableActions.ts b/lib/packages/shared-utils/package-actions/getAvailableActions.ts index eba5370e2c..c6a21a755f 100644 --- a/lib/packages/shared-utils/package-actions/getAvailableActions.ts +++ b/lib/packages/shared-utils/package-actions/getAvailableActions.ts @@ -1,6 +1,6 @@ import { Action, CognitoUserAttributes, opensearch } from "../../shared-types"; -import rules from "./rules"; import { PackageCheck } from "../package-check"; +import rules from "./rules"; export const getAvailableActions = ( user: CognitoUserAttributes, @@ -9,6 +9,7 @@ export const getAvailableActions = ( const allActions: Action[][] = []; const allMembers = [result]; + if (result.appkChildren) { allMembers.push(...result.appkChildren.map((el) => el._source)); } @@ -27,17 +28,13 @@ export const getAvailableActions = ( return acc.filter((action: Action) => currentActions.includes(action)); }, []); - const allRaiRequestedDates = allMembers.map((member) => { - return member.raiRequestedDate; - }); - const isRaiRequestedDateIdentical = allRaiRequestedDates.every( - (date, _, arr) => date === arr[0], - ); + const allRaiRequestedDates = allMembers + .filter((member) => (member as opensearch.main.SeatoolDocument)?.raiRequestedDate !== undefined) + .map((member) => (member as opensearch.main.SeatoolDocument)?.raiRequestedDate); + const isRaiRequestedDateIdentical = allRaiRequestedDates.every((date, _, arr) => date === arr[0]); if (!isRaiRequestedDateIdentical) { const actionsToRemove = [Action.RESPOND_TO_RAI, Action.WITHDRAW_RAI]; - commonActions = commonActions.filter( - (action: any) => !actionsToRemove.includes(action), - ); + commonActions = commonActions.filter((action: any) => !actionsToRemove.includes(action)); } return commonActions; }; diff --git a/lib/packages/shared-utils/package-actions/rules.ts b/lib/packages/shared-utils/package-actions/rules.ts index 0949e68fa5..efb60e1373 100644 --- a/lib/packages/shared-utils/package-actions/rules.ts +++ b/lib/packages/shared-utils/package-actions/rules.ts @@ -47,7 +47,6 @@ const arEnableWithdrawRaiResponse: ActionRule = { checker.hasRaiResponse && !checker.hasEnabledRaiWithdraw && isCmsWriteUser(user) && - checker.hasStatus(SEATOOL_STATUS.PENDING_RAI) && !checker.hasStatus(finalDispositionStatuses) ); } diff --git a/lib/packages/shared-utils/package-check.ts b/lib/packages/shared-utils/package-check.ts index 25ae9a89b8..c03b4afb33 100644 --- a/lib/packages/shared-utils/package-check.ts +++ b/lib/packages/shared-utils/package-check.ts @@ -1,14 +1,14 @@ import { - opensearch, - Authority, - SEATOOL_STATUS, - ActionType, Action, + ActionType, + Authority, CognitoUserAttributes, + opensearch, + SEATOOL_STATUS, } from "shared-types"; -const checkAuthority = (authority: Authority | null, validAuthorities: Authority[]) => - !authority ? false : validAuthorities.includes(authority.toLowerCase() as Authority); +const checkAuthority = (authority: Authority | string | null, validAuthorities: string[]) => + !authority ? false : validAuthorities.includes(authority.toLowerCase()); const checkStatus = (seatoolStatus: string, authorized: string | string[]) => typeof authorized === "string" @@ -44,7 +44,7 @@ export const PackageCheck = ({ isAppk: false, isAppkChild: false, /** Keep excess methods to a minimum with `is` **/ - authorityIs: (validAuthorities: Authority[]) => checkAuthority(authority, validAuthorities), + authorityIs: (validAuthorities: string[]) => checkAuthority(authority, validAuthorities), hasCpoc: !!leadAnalystName, }; const statusChecks = { diff --git a/lib/packages/shared-utils/secrets-manager.test.ts b/lib/packages/shared-utils/secrets-manager.test.ts index ae9846eb6c..a1e3d8c933 100644 --- a/lib/packages/shared-utils/secrets-manager.test.ts +++ b/lib/packages/shared-utils/secrets-manager.test.ts @@ -1,65 +1,34 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; +import { + TEST_SECRET_ERROR_ID, + TEST_SECRET_ID, + TEST_SECRET_NO_VALUE_ID, + TEST_SECRET_TO_DELETE_ID, +} from "mocks"; +import secrets from "mocks/data/secrets"; +import { describe, expect, it } from "vitest"; import { getSecret } from "."; -const mockSend = vi.fn(); - -vi.mock("@aws-sdk/client-secrets-manager", () => { - return { - SecretsManagerClient: vi.fn(() => ({ - send: mockSend, - })), - GetSecretValueCommand: vi.fn(), - DescribeSecretCommand: vi.fn(), - }; -}); - describe("getSecret", () => { - beforeEach(() => { - mockSend.mockClear(); - }); - it("should return the secret value if the secret exists and is not marked for deletion", async () => { - const secretId = "test-secret"; - const expectedSecretValue = "test-secret-value"; // pragma: allowlist secret - - mockSend - .mockResolvedValueOnce({ DeletedDate: null }) // Mock DescribeSecretCommand response - .mockResolvedValueOnce({ SecretString: expectedSecretValue }); // Mock GetSecretValueCommand response - - const result = await getSecret(secretId); - expect(result).toBe(expectedSecretValue); + const result = await getSecret(TEST_SECRET_ID); + expect(result).toBe(secrets[TEST_SECRET_ID].SecretString); }); it("should throw an error if the secret is marked for deletion", async () => { - const secretId = "test-secret"; - - mockSend.mockResolvedValueOnce({ DeletedDate: new Date() }); // Mock DescribeSecretCommand response - - await expect(getSecret(secretId)).rejects.toThrow( - `Secret ${secretId} is marked for deletion and will not be used.`, + await expect(getSecret(TEST_SECRET_TO_DELETE_ID)).rejects.toThrow( + `Secret ${TEST_SECRET_TO_DELETE_ID} is marked for deletion and will not be used.`, ); }); it("should throw an error if the secret has no SecretString field", async () => { - const secretId = "test-secret"; - - mockSend - .mockResolvedValueOnce({ DeletedDate: null }) // Mock DescribeSecretCommand response - .mockResolvedValueOnce({ SecretString: null }); // Mock GetSecretValueCommand response - - await expect(getSecret(secretId)).rejects.toThrow( - `Secret ${secretId} has no SecretString field present in response`, + await expect(getSecret(TEST_SECRET_NO_VALUE_ID)).rejects.toThrow( + `Secret ${TEST_SECRET_NO_VALUE_ID} has no SecretString field present in response`, ); }); it("should throw an error if there is an issue with the AWS SDK call", async () => { - const secretId = "test-secret"; - const errorMessage = "AWS SDK error"; - - mockSend.mockRejectedValueOnce(new Error(errorMessage)); // Mock DescribeSecretCommand failure - - await expect(getSecret(secretId)).rejects.toThrow( - `Failed to fetch secret ${secretId}: Error: ${errorMessage}`, + await expect(getSecret(TEST_SECRET_ERROR_ID)).rejects.toThrow( + `Failed to fetch secret ${TEST_SECRET_ERROR_ID}: Secret Throw Get Secret Error has no SecretString field present in response`, ); }); }); diff --git a/lib/packages/shared-utils/testData.ts b/lib/packages/shared-utils/testData.ts index 90dbd569b0..77e56fa3d0 100644 --- a/lib/packages/shared-utils/testData.ts +++ b/lib/packages/shared-utils/testData.ts @@ -1,4 +1,4 @@ -import { opensearch, Authority } from "shared-types"; +import { Authority, opensearch } from "shared-types"; import { OneMacUser } from "ui/src/api"; export const testStateCognitoUser: OneMacUser = { @@ -91,7 +91,7 @@ export const testItemResult: opensearch.main.ItemResult = { raiReceivedDate: null, raiRequestedDate: null, leadAnalystOfficerId: null, - proposedDate: "2024-03-30T00:00:00.000Z", + // proposedDate: "2024-03-30T00:00:00.000Z", // TODO state: "MD", raiWithdrawnDate: null, finalDispositionDate: null, @@ -114,7 +114,6 @@ export const testItemResult: opensearch.main.ItemResult = { _source: { authority: "medicaid spa", origin: "mako", - //@ts-expect-error appkParentId: null, additionalInformation: "does the main branch work?!", submitterName: "George Harrison", @@ -129,8 +128,7 @@ export const testItemResult: opensearch.main.ItemResult = { uploadDate: 1709319909222, }, { - filename: - "10-20-17 MT 1915(b)(4) Big Sky Waiver app revised (6).docx", + filename: "10-20-17 MT 1915(b)(4) Big Sky Waiver app revised (6).docx", title: "SPA Pages", bucket: "test-bucket", key: "test-key", @@ -138,7 +136,6 @@ export const testItemResult: opensearch.main.ItemResult = { }, ], raiWithdrawEnabled: false, - //@ts-expect-error actionType: "new-submission", //@ts-expect-error timestamp: "1709319909826", diff --git a/lib/packages/shared-utils/user-helper.ts b/lib/packages/shared-utils/user-helper.ts index 58983f9d65..7a5266317a 100644 --- a/lib/packages/shared-utils/user-helper.ts +++ b/lib/packages/shared-utils/user-helper.ts @@ -1,7 +1,7 @@ import { + CMS_READ_ONLY_ROLES, CMS_ROLES, CMS_WRITE_ROLES, - CMS_READ_ONLY_ROLES, CognitoUserAttributes, STATE_ROLES, UserRoles, @@ -9,23 +9,20 @@ import { /** Function receives a user's cognito attributes and list of authorized roles, * and will confirm the user has one or more authorized UserRoles */ -const userHasAuthorizedRole = ( - user: CognitoUserAttributes | null, - authorized: UserRoles[], -) => { +const userHasAuthorizedRole = (user: CognitoUserAttributes | null, authorized: UserRoles[]) => { if (!user) return false; const userRoles = user["custom:cms-roles"].split(",") as UserRoles[]; return userRoles.filter((role) => authorized.includes(role)).length > 0; }; /** Confirms user is any kind of CMS user */ -export const isCmsUser = (user: CognitoUserAttributes) => +export const isCmsUser = (user: CognitoUserAttributes | null) => userHasAuthorizedRole(user, CMS_ROLES); /** Confirms user is a CMS user who can create data */ -export const isCmsWriteUser = (user: CognitoUserAttributes) => +export const isCmsWriteUser = (user: CognitoUserAttributes | null) => userHasAuthorizedRole(user, CMS_WRITE_ROLES); /** Confirms user is a CMS user who can only view data */ -export const isCmsReadonlyUser = (user: CognitoUserAttributes) => +export const isCmsReadonlyUser = (user: CognitoUserAttributes | null) => userHasAuthorizedRole(user, CMS_READ_ONLY_ROLES); /** Confirms user is a State user */ export const isStateUser = (user: CognitoUserAttributes | null) => @@ -34,5 +31,4 @@ export const isStateUser = (user: CognitoUserAttributes | null) => export const isCmsSuperUser = (user: CognitoUserAttributes | null) => userHasAuthorizedRole(user, [UserRoles.CMS_SUPER_USER]); /** Confirms user is an IDM user */ -export const isIDM = (user: CognitoUserAttributes | null) => - user?.username.startsWith("IDM_"); +export const isIDM = (user: CognitoUserAttributes | null) => user?.username.startsWith("IDM_"); diff --git a/lib/vitest.config.ts b/lib/vitest.config.ts new file mode 100644 index 0000000000..7f7947edcd --- /dev/null +++ b/lib/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineProject } from "vitest/config"; + +export default defineProject({ + test: { + root: ".", + setupFiles: ["./vitest.setup.ts"], + exclude: ["**/node_modules/**", "libs/email/content/**"], + environment: "node", + }, +}); diff --git a/lib/vitest.setup.ts b/lib/vitest.setup.ts new file mode 100644 index 0000000000..4e0a962fba --- /dev/null +++ b/lib/vitest.setup.ts @@ -0,0 +1,102 @@ +import { Amplify } from "aws-amplify"; +import { + API_CONFIG, + API_ENDPOINT, + AUTH_CONFIG, + IDENTITY_POOL_ID, + OPENSEARCH_DOMAIN, + OPENSEARCH_INDEX_NAMESPACE, + PROJECT, + REGION, + STAGE, + USER_POOL_CLIENT_DOMAIN, + USER_POOL_CLIENT_ID, + USER_POOL_ID, + setDefaultStateSubmitter, +} from "mocks"; +import { mockedServer } from "mocks/server"; +import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest"; + +// TODO to mock +// defaultApiTokenHandler: {} +// [MSW] Warning: intercepted a request without a matching request handler: +// • GET http://169.254.169.254/latest/meta-data/iam/security-credentials/ + +// starting MSW listener for lib tests +// stdout | local-constructs/manage-users/src/manageUsers.test.ts > Cognito User Lambda Handler > should handle errors and send FAILED response +// Error: Failed to get secret +// at /home/runner/work/macpro-mako/macpro-mako/lib/local-constructs/manage-users/src/manageUsers.test.ts:126:37 +// at file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:146:14 +// at file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:529:11 +// at runWithTimeout (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:61:7) +// at runTest (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:982:17) +// at processTicksAndRejections (node:internal/process/task_queues:95:5) +// at runSuite (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1131:15) +// at runSuite (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1131:15) +// at runFiles (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1188:5) +// at startTests (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1197:3) +// ✓ |lib| local-constructs/manage-users/src/manageUsers.test.ts (2 tests) 15ms + +// { username: '53832e35-1fbe-4c74-9111-4a0cd29ce2cf' } +// getAuthDetails event: {"requestContext":{"identity":{"cognitoAuthenticationProvider":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_userPool1,https://cognito-idp.us-east-1.amazonaws.com/us-east-1_userPool1:CognitoSignIn:53832e35-1fbe-4c74-9111-4a0cd29ce2cf"}}} +// { +// authDetails: { +// userId: '53832e35-1fbe-4c74-9111-4a0cd29ce2cf', +// poolId: 'us-east-1_userPool1' +// } +// } +// defaultApiTokenHandler: {} +// defaultSecurityCredentialsHandler: {} +// defaultSecurityCredentialsHandler: {} + +Amplify.configure({ + API: API_CONFIG, + Auth: AUTH_CONFIG, +}); + +beforeAll(() => { + setDefaultStateSubmitter(); + + vi.spyOn(console, "error").mockImplementation(() => {}); + + console.log("starting MSW listener for lib tests"); + mockedServer.listen({ + onUnhandledRequest: "warn", + }); +}); + +beforeEach(() => { + process.env.PROJECT = PROJECT; + process.env.REGION_A = REGION; + process.env.STAGE = STAGE; + process.env.isDev = "true"; + + process.env.project = PROJECT; + process.env.region = REGION; + process.env.stage = STAGE; + + process.env.applicationEndpointUrl = API_ENDPOINT; + process.env.identityPoolId = IDENTITY_POOL_ID; + process.env.userPoolId = USER_POOL_ID; + process.env.idmClientId = USER_POOL_CLIENT_ID; + process.env.idmClientIssuer = USER_POOL_CLIENT_DOMAIN; + process.env.osDomain = OPENSEARCH_DOMAIN; + process.env.indexNamespace = OPENSEARCH_INDEX_NAMESPACE; +}); + +afterEach(() => { + vi.useRealTimers(); + vi.clearAllMocks(); + + setDefaultStateSubmitter(); + // Reset any request handlers that we may add during the tests, + // so they don't affect other tests. + mockedServer.resetHandlers(); +}); + +afterAll(() => { + vi.clearAllMocks(); + + // Clean up after the tests are finished. + mockedServer.close(); +}); diff --git a/mocks/browser.ts b/mocks/browser.ts new file mode 100644 index 0000000000..1a0434a512 --- /dev/null +++ b/mocks/browser.ts @@ -0,0 +1,4 @@ +import { setupWorker } from "msw/browser"; +import handlers from "./handlers"; + +export const mockedWorker = setupWorker(...handlers); diff --git a/mocks/consts.ts b/mocks/consts.ts new file mode 100644 index 0000000000..9023b452fa --- /dev/null +++ b/mocks/consts.ts @@ -0,0 +1,39 @@ +export const PROJECT = "mako"; +export const REGION = "us-east-1"; +export const STAGE = "mocked-tests"; +export const API_ENDPOINT = `https://test-domain.execute-api.${REGION}.amazonaws.com/mocked-tests`; +export const IDENTITY_POOL_ID = `${REGION}:test-identity-pool-id`; +export const USER_POOL_ID = `${REGION}_userPool1`; +export const USER_POOL_CLIENT_ID = "userPoolWebClientId"; +export const USER_POOL_CLIENT_DOMAIN = `mocked-tests-login-${USER_POOL_CLIENT_ID}.auth.${REGION}.amazoncognito.com`; +export const COGNITO_IDP_DOMAIN = `https://cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}`; +export const OPENSEARCH_DOMAIN = `https://vpc-opensearchdomain-mock-domain.${REGION}.es.amazonaws.com`; +export const OPENSEARCH_INDEX_NAMESPACE = "test-namespace-"; + +export const ACCESS_KEY_ID = "ASIAZHXA3XOU7XZ53M36"; // pragma: allowlist secret +export const SECRET_KEY = "UWKCFxhrgbPnixgLnL1JKwFEwiK9ZKvTAtpk8cGa"; // pragma: allowlist secret + +export const API_CONFIG = { + endpoints: [ + { + name: "os", + endpoint: API_ENDPOINT, + region: REGION, + }, + ], +}; + +export const AUTH_CONFIG = { + mandatorySignIn: true, + region: REGION, + userPoolId: USER_POOL_ID, + identityPoolId: IDENTITY_POOL_ID, + userPoolWebClientId: USER_POOL_CLIENT_ID, + oauth: { + domain: USER_POOL_CLIENT_DOMAIN, + redirectSignIn: "http://localhost", + redirectSignOut: "http://localhost", + scope: ["email", "openid"], + responseType: "code", + }, +}; diff --git a/mocks/data/cloudFormationsExports.ts b/mocks/data/cloudFormationsExports.ts new file mode 100644 index 0000000000..db673ac7b8 --- /dev/null +++ b/mocks/data/cloudFormationsExports.ts @@ -0,0 +1,14 @@ +import { TestExport } from "../index.d"; + +export const TEST_CF_EXPORT_ID = "test-export"; +export const TEST_CF_EXPORT_NOT_FOUND_ID = "missing-test-export"; + +const exportsList: TestExport[] = [ + { + ExportingStackId: "test-stack-id", + Name: "test-export", + Value: "test-value", + }, +]; + +export default exportsList; diff --git a/mocks/data/counties.ts b/mocks/data/counties.ts new file mode 100644 index 0000000000..44eec53b8d --- /dev/null +++ b/mocks/data/counties.ts @@ -0,0 +1,260 @@ +import { TestCounty } from "../index.d"; + +const counties: Record = { + "24": [ + ["NAME", "state", "county"], + ["Worcester County, Maryland", "24", "047"], + ["Talbot County, Maryland", "24", "041"], + ["Howard County, Maryland", "24", "027"], + ["Prince George's County, Maryland", "24", "033"], + ["Anne Arundel County, Maryland", "24", "003"], + ["Baltimore County, Maryland", "24", "005"], + ["Frederick County, Maryland", "24", "021"], + ["Calvert County, Maryland", "24", "009"], + ["Garrett County, Maryland", "24", "023"], + ["Kent County, Maryland", "24", "029"], + ["Montgomery County, Maryland", "24", "031"], + ["Carroll County, Maryland", "24", "013"], + ["Queen Anne's County, Maryland", "24", "035"], + ["St. Mary's County, Maryland", "24", "037"], + ["Charles County, Maryland", "24", "017"], + ["Dorchester County, Maryland", "24", "019"], + ["Washington County, Maryland", "24", "043"], + ["Wicomico County, Maryland", "24", "045"], + ["Cecil County, Maryland", "24", "015"], + ["Caroline County, Maryland", "24", "011"], + ["Baltimore city, Maryland", "24", "510"], + ["Somerset County, Maryland", "24", "039"], + ["Harford County, Maryland", "24", "025"], + ["Allegany County, Maryland", "24", "001"], + ], + "39": [ + ["NAME", "state", "county"], + ["Meigs County, Ohio", "39", "105"], + ["Champaign County, Ohio", "39", "021"], + ["Greene County, Ohio", "39", "057"], + ["Lawrence County, Ohio", "39", "087"], + ["Morgan County, Ohio", "39", "115"], + ["Wyandot County, Ohio", "39", "175"], + ["Erie County, Ohio", "39", "043"], + ["Logan County, Ohio", "39", "091"], + ["Summit County, Ohio", "39", "153"], + ["Shelby County, Ohio", "39", "149"], + ["Williams County, Ohio", "39", "171"], + ["Columbiana County, Ohio", "39", "029"], + ["Fulton County, Ohio", "39", "051"], + ["Gallia County, Ohio", "39", "053"], + ["Mahoning County, Ohio", "39", "099"], + ["Scioto County, Ohio", "39", "145"], + ["Morrow County, Ohio", "39", "117"], + ["Preble County, Ohio", "39", "135"], + ["Allen County, Ohio", "39", "003"], + ["Crawford County, Ohio", "39", "033"], + ["Guernsey County, Ohio", "39", "059"], + ["Mercer County, Ohio", "39", "107"], + ["Clark County, Ohio", "39", "023"], + ["Lorain County, Ohio", "39", "093"], + ["Montgomery County, Ohio", "39", "113"], + ["Warren County, Ohio", "39", "165"], + ["Ashtabula County, Ohio", "39", "007"], + ["Union County, Ohio", "39", "159"], + ["Pike County, Ohio", "39", "131"], + ["Paulding County, Ohio", "39", "125"], + ["Hamilton County, Ohio", "39", "061"], + ["Franklin County, Ohio", "39", "049"], + ["Marion County, Ohio", "39", "101"], + ["Tuscarawas County, Ohio", "39", "157"], + ["Wayne County, Ohio", "39", "169"], + ["Brown County, Ohio", "39", "015"], + ["Butler County, Ohio", "39", "017"], + ["Harrison County, Ohio", "39", "067"], + ["Knox County, Ohio", "39", "083"], + ["Licking County, Ohio", "39", "089"], + ["Richland County, Ohio", "39", "139"], + ["Vinton County, Ohio", "39", "163"], + ["Carroll County, Ohio", "39", "019"], + ["Hancock County, Ohio", "39", "063"], + ["Clermont County, Ohio", "39", "025"], + ["Fairfield County, Ohio", "39", "045"], + ["Defiance County, Ohio", "39", "039"], + ["Highland County, Ohio", "39", "071"], + ["Henry County, Ohio", "39", "069"], + ["Jefferson County, Ohio", "39", "081"], + ["Portage County, Ohio", "39", "133"], + ["Putnam County, Ohio", "39", "137"], + ["Belmont County, Ohio", "39", "013"], + ["Clinton County, Ohio", "39", "027"], + ["Fayette County, Ohio", "39", "047"], + ["Perry County, Ohio", "39", "127"], + ["Pickaway County, Ohio", "39", "129"], + ["Madison County, Ohio", "39", "097"], + ["Muskingum County, Ohio", "39", "119"], + ["Sandusky County, Ohio", "39", "143"], + ["Van Wert County, Ohio", "39", "161"], + ["Washington County, Ohio", "39", "167"], + ["Auglaize County, Ohio", "39", "011"], + ["Darke County, Ohio", "39", "037"], + ["Ottawa County, Ohio", "39", "123"], + ["Hardin County, Ohio", "39", "065"], + ["Jackson County, Ohio", "39", "079"], + ["Huron County, Ohio", "39", "077"], + ["Athens County, Ohio", "39", "009"], + ["Adams County, Ohio", "39", "001"], + ["Medina County, Ohio", "39", "103"], + ["Lucas County, Ohio", "39", "095"], + ["Noble County, Ohio", "39", "121"], + ["Seneca County, Ohio", "39", "147"], + ["Stark County, Ohio", "39", "151"], + ["Coshocton County, Ohio", "39", "031"], + ["Delaware County, Ohio", "39", "041"], + ["Geauga County, Ohio", "39", "055"], + ["Hocking County, Ohio", "39", "073"], + ["Lake County, Ohio", "39", "085"], + ["Ross County, Ohio", "39", "141"], + ["Cuyahoga County, Ohio", "39", "035"], + ["Holmes County, Ohio", "39", "075"], + ["Miami County, Ohio", "39", "109"], + ["Monroe County, Ohio", "39", "111"], + ["Trumbull County, Ohio", "39", "155"], + ["Wood County, Ohio", "39", "173"], + ["Ashland County, Ohio", "39", "005"], + ], + "51": [ + ["NAME", "state", "county"], + ["Surry County, Virginia", "51", "181"], + ["Clarke County, Virginia", "51", "043"], + ["Mecklenburg County, Virginia", "51", "117"], + ["Augusta County, Virginia", "51", "015"], + ["Goochland County, Virginia", "51", "075"], + ["Madison County, Virginia", "51", "113"], + ["Roanoke County, Virginia", "51", "161"], + ["Brunswick County, Virginia", "51", "025"], + ["Northampton County, Virginia", "51", "131"], + ["Rockbridge County, Virginia", "51", "163"], + ["Norton city, Virginia", "51", "720"], + ["Colonial Heights city, Virginia", "51", "570"], + ["Bland County, Virginia", "51", "021"], + ["Botetourt County, Virginia", "51", "023"], + ["Bristol city, Virginia", "51", "520"], + ["Williamsburg city, Virginia", "51", "830"], + ["Carroll County, Virginia", "51", "035"], + ["Washington County, Virginia", "51", "191"], + ["Page County, Virginia", "51", "139"], + ["Alexandria city, Virginia", "51", "510"], + ["Hampton city, Virginia", "51", "650"], + ["Arlington County, Virginia", "51", "013"], + ["Buchanan County, Virginia", "51", "027"], + ["Stafford County, Virginia", "51", "179"], + ["Richmond city, Virginia", "51", "760"], + ["Essex County, Virginia", "51", "057"], + ["Tazewell County, Virginia", "51", "185"], + ["Manassas Park city, Virginia", "51", "685"], + ["Sussex County, Virginia", "51", "183"], + ["Manassas city, Virginia", "51", "683"], + ["Salem city, Virginia", "51", "775"], + ["Charlotte County, Virginia", "51", "037"], + ["Greensville County, Virginia", "51", "081"], + ["Isle of Wight County, Virginia", "51", "093"], + ["Franklin city, Virginia", "51", "620"], + ["Lexington city, Virginia", "51", "678"], + ["Henry County, Virginia", "51", "089"], + ["King William County, Virginia", "51", "101"], + ["Louisa County, Virginia", "51", "109"], + ["Prince Edward County, Virginia", "51", "147"], + ["Rockingham County, Virginia", "51", "165"], + ["James City County, Virginia", "51", "095"], + ["Russell County, Virginia", "51", "167"], + ["Smyth County, Virginia", "51", "173"], + ["Charlottesville city, Virginia", "51", "540"], + ["Campbell County, Virginia", "51", "031"], + ["Henrico County, Virginia", "51", "087"], + ["King George County, Virginia", "51", "099"], + ["Lancaster County, Virginia", "51", "103"], + ["Lee County, Virginia", "51", "105"], + ["Mathews County, Virginia", "51", "115"], + ["Middlesex County, Virginia", "51", "119"], + ["Prince George County, Virginia", "51", "149"], + ["Richmond County, Virginia", "51", "159"], + ["Scott County, Virginia", "51", "169"], + ["Spotsylvania County, Virginia", "51", "177"], + ["Appomattox County, Virginia", "51", "011"], + ["Franklin County, Virginia", "51", "067"], + ["King and Queen County, Virginia", "51", "097"], + ["Montgomery County, Virginia", "51", "121"], + ["New Kent County, Virginia", "51", "127"], + ["Cumberland County, Virginia", "51", "049"], + ["Dickenson County, Virginia", "51", "051"], + ["Halifax County, Virginia", "51", "083"], + ["Loudoun County, Virginia", "51", "107"], + ["Prince William County, Virginia", "51", "153"], + ["Southampton County, Virginia", "51", "175"], + ["Highland County, Virginia", "51", "091"], + ["Caroline County, Virginia", "51", "033"], + ["Gloucester County, Virginia", "51", "073"], + ["Patrick County, Virginia", "51", "141"], + ["Powhatan County, Virginia", "51", "145"], + ["Shenandoah County, Virginia", "51", "171"], + ["Fauquier County, Virginia", "51", "061"], + ["Staunton city, Virginia", "51", "790"], + ["Fluvanna County, Virginia", "51", "065"], + ["Radford city, Virginia", "51", "750"], + ["Winchester city, Virginia", "51", "840"], + ["Accomack County, Virginia", "51", "001"], + ["Craig County, Virginia", "51", "045"], + ["Floyd County, Virginia", "51", "063"], + ["Giles County, Virginia", "51", "071"], + ["Grayson County, Virginia", "51", "077"], + ["Nottoway County, Virginia", "51", "135"], + ["Rappahannock County, Virginia", "51", "157"], + ["Charles City County, Virginia", "51", "036"], + ["Culpeper County, Virginia", "51", "047"], + ["Dinwiddie County, Virginia", "51", "053"], + ["Greene County, Virginia", "51", "079"], + ["Hanover County, Virginia", "51", "085"], + ["Lunenburg County, Virginia", "51", "111"], + ["Orange County, Virginia", "51", "137"], + ["Emporia city, Virginia", "51", "595"], + ["Wythe County, Virginia", "51", "197"], + ["Chesapeake city, Virginia", "51", "550"], + ["Harrisonburg city, Virginia", "51", "660"], + ["Albemarle County, Virginia", "51", "003"], + ["Bedford County, Virginia", "51", "019"], + ["Buckingham County, Virginia", "51", "029"], + ["Wise County, Virginia", "51", "195"], + ["Covington city, Virginia", "51", "580"], + ["Portsmouth city, Virginia", "51", "740"], + ["Virginia Beach city, Virginia", "51", "810"], + ["Alleghany County, Virginia", "51", "005"], + ["Galax city, Virginia", "51", "640"], + ["Westmoreland County, Virginia", "51", "193"], + ["Northumberland County, Virginia", "51", "133"], + ["Pulaski County, Virginia", "51", "155"], + ["Warren County, Virginia", "51", "187"], + ["Danville city, Virginia", "51", "590"], + ["Fredericksburg city, Virginia", "51", "630"], + ["Amherst County, Virginia", "51", "009"], + ["Bath County, Virginia", "51", "017"], + ["Hopewell city, Virginia", "51", "670"], + ["Newport News city, Virginia", "51", "700"], + ["Frederick County, Virginia", "51", "069"], + ["Lynchburg city, Virginia", "51", "680"], + ["York County, Virginia", "51", "199"], + ["Martinsville city, Virginia", "51", "690"], + ["Nelson County, Virginia", "51", "125"], + ["Pittsylvania County, Virginia", "51", "143"], + ["Buena Vista city, Virginia", "51", "530"], + ["Falls Church city, Virginia", "51", "610"], + ["Petersburg city, Virginia", "51", "730"], + ["Poquoson city, Virginia", "51", "735"], + ["Fairfax city, Virginia", "51", "600"], + ["Waynesboro city, Virginia", "51", "820"], + ["Roanoke city, Virginia", "51", "770"], + ["Suffolk city, Virginia", "51", "800"], + ["Norfolk city, Virginia", "51", "710"], + ["Amelia County, Virginia", "51", "007"], + ["Chesterfield County, Virginia", "51", "041"], + ["Fairfax County, Virginia", "51", "059"], + ], +}; + +export default counties; diff --git a/mocks/data/index.ts b/mocks/data/index.ts new file mode 100644 index 0000000000..ddcb36eb91 --- /dev/null +++ b/mocks/data/index.ts @@ -0,0 +1,6 @@ +export * from "./cloudFormationsExports"; +export * from "./counties"; +export * from "./items"; +export * from "./secrets"; +export * from "./types"; +export * from "./users"; diff --git a/mocks/data/items.ts b/mocks/data/items.ts new file mode 100644 index 0000000000..d302cfdac3 --- /dev/null +++ b/mocks/data/items.ts @@ -0,0 +1,344 @@ +import { SEATOOL_STATUS } from "shared-types"; +import type { TestItemResult } from "../index.d"; + +export const EXISTING_ITEM_PENDING_ID = "MD-0002.R00.00"; +export const EXISTING_ITEM_APPROVED_NEW_ID = "MD-0000.R00.00"; +export const VALID_ITEM_TEMPORARY_EXTENSION_ID = "MD-0000.R00.TE00"; +export const EXISTING_ITEM_APPROVED_AMEND_ID = "MD-0000.R00.01"; +export const EXISTING_ITEM_APPROVED_RENEW_ID = "MD-0000.R01.00"; +export const EXISTING_ITEM_ID = "MD-00-0000"; +export const NOT_FOUND_ITEM_ID = "MD-0004.R00.00"; +export const NOT_EXISTING_ITEM_ID = "MD-11-0000" +export const TEST_ITEM_ID = "MD-0005.R01.00"; +export const EXISTING_ITEM_TEMPORARY_EXTENSION_ID = "MD-0005.R01.TE00"; +export const HI_TEST_ITEM_ID = "HI-0000.R00.00"; +export const CAPITATED_INITIAL_ITEM_ID = "MD-006.R00.00"; +export const CAPITATED_AMEND_ITEM_ID = "MD-006.R00.01"; +export const CONTRACTING_INITIAL_ITEM_ID = "MD-007.R00.00"; +export const CONTRACTING_AMEND_ITEM_ID = "MD-007.R00.01"; +export const MISSING_CHANGELOG_ITEM_ID = "MD-008.R00.00"; +export const WITHDRAWN_CHANGELOG_ITEM_ID = "MD-009.R00.01"; +export const INITIAL_RELEASE_APPK_ITEM_ID = "MD-010.R00.01"; +export const SUBMISSION_ERROR_ITEM_ID = "Throw Submission Error"; +export const GET_ERROR_ITEM_ID = "Throw Get Item Error"; + +const items: Record = { + [EXISTING_ITEM_ID]: { + _id: EXISTING_ITEM_ID, + found: true, + _source: { + id: EXISTING_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + }, + }, + [EXISTING_ITEM_APPROVED_NEW_ID]: { + _id: EXISTING_ITEM_APPROVED_NEW_ID, + found: true, + _source: { + id: EXISTING_ITEM_APPROVED_NEW_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + authority: "1915(b)", + origin: "OneMAC", + state: "MD", + }, + }, + [EXISTING_ITEM_APPROVED_AMEND_ID]: { + _id: EXISTING_ITEM_APPROVED_AMEND_ID, + found: true, + _source: { + id: EXISTING_ITEM_APPROVED_AMEND_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Amend", + authority: "1915(b)", + origin: "OneMAC", + state: "MD", + }, + }, + [EXISTING_ITEM_APPROVED_RENEW_ID]: { + _id: EXISTING_ITEM_APPROVED_RENEW_ID, + found: true, + _source: { + id: EXISTING_ITEM_APPROVED_RENEW_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Renew", + authority: "1915(b)", + origin: "OneMAC", + state: "MD", + }, + }, + [EXISTING_ITEM_PENDING_ID]: { + _id: EXISTING_ITEM_PENDING_ID, + found: true, + _source: { + id: EXISTING_ITEM_PENDING_ID, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + origin: "SEATool", + state: "MD", + }, + }, + [NOT_FOUND_ITEM_ID]: { + _id: NOT_FOUND_ITEM_ID, + found: false, + }, + [TEST_ITEM_ID]: { + _id: TEST_ITEM_ID, + found: true, + _source: { + id: TEST_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + state: "MD", + origin: "OneMAC", + changelog: [ + { + _id: `${TEST_ITEM_ID}-001`, + _source: { + id: `${TEST_ITEM_ID}-0001`, + event: "new-medicaid-submission", + packageId: TEST_ITEM_ID, + }, + }, + ], + authority: "Medicaid SPA", + }, + }, + [EXISTING_ITEM_TEMPORARY_EXTENSION_ID]: { + _id: EXISTING_ITEM_TEMPORARY_EXTENSION_ID, + found: true, + _source: { + id: EXISTING_ITEM_TEMPORARY_EXTENSION_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Amend", + authority: "Medicaid SPA", + origin: "OneMAC", + state: "MD", + }, + }, + [HI_TEST_ITEM_ID]: { + _id: HI_TEST_ITEM_ID, + found: true, + _source: { + id: HI_TEST_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + authority: "Medicaid SPA", + state: "HI", + origin: "OneMAC", + }, + }, + [CAPITATED_INITIAL_ITEM_ID]: { + _id: CAPITATED_INITIAL_ITEM_ID, + found: true, + _source: { + id: CAPITATED_INITIAL_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Amend", + authority: "1915(b)", + origin: "OneMAC", + state: "MD", + changelog: [ + { + _id: `${CAPITATED_INITIAL_ITEM_ID}-0001`, + _source: { + id: `${CAPITATED_INITIAL_ITEM_ID}-0001`, + event: "capitated-initial", + packageId: CAPITATED_INITIAL_ITEM_ID, + }, + }, + ], + }, + }, + [CONTRACTING_INITIAL_ITEM_ID]: { + _id: CONTRACTING_INITIAL_ITEM_ID, + found: true, + _source: { + id: CONTRACTING_INITIAL_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Amend", + authority: "1915(b)", + origin: "OneMAC", + state: "MD", + changelog: [ + { + _id: `${CONTRACTING_INITIAL_ITEM_ID}-0001`, + _source: { + id: `${CONTRACTING_INITIAL_ITEM_ID}-0001`, + event: "contracting-initial", + packageId: CONTRACTING_INITIAL_ITEM_ID, + }, + }, + ], + }, + }, + [MISSING_CHANGELOG_ITEM_ID]: { + _id: MISSING_CHANGELOG_ITEM_ID, + found: true, + _source: { + id: MISSING_CHANGELOG_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "Amend", + authority: "1915(b)", + origin: "OneMAC", + state: "MD", + changelog: [], + }, + }, + [WITHDRAWN_CHANGELOG_ITEM_ID]: { + _id: WITHDRAWN_CHANGELOG_ITEM_ID, + found: true, + _source: { + id: WITHDRAWN_CHANGELOG_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.WITHDRAWN, + actionType: "Withdrawal", + authority: "CHIP SPA", + state: "MD", + origin: "OneMAC", + changelog: [ + { + _id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0001`, + _source: { + packageId: WITHDRAWN_CHANGELOG_ITEM_ID, + id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0001`, + event: "capitated-amendment", + attachments: [ + { + key: "doc001", + title: "Contract Amendment", + filename: "contract_amendment_2024.pdf", + }, + ], + additionalInformation: "Amendment to the capitated contract terms for 2024.", + timestamp: 1672531200000, // Jan 1, 2023, in milliseconds + }, + }, + { + _id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0002`, + _source: { + packageId: WITHDRAWN_CHANGELOG_ITEM_ID, + id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0002`, + event: "respond-to-rai", + attachments: [ + { + key: "rai002", + title: "Response to RAI", + filename: "rai_response.docx", + }, + ], + additionalInformation: "Detailed response to the request for additional information.", + timestamp: 1675123200000, // Feb 1, 2023 + }, + }, + { + _id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0003`, + _source: { + packageId: WITHDRAWN_CHANGELOG_ITEM_ID, + id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0003`, + event: "upload-subsequent-documents", + attachments: [ + { + key: "subdoc003", + title: "Follow-Up Documents", + filename: "followup_docs.zip", + }, + ], + additionalInformation: "Supporting documents uploaded as follow-up.", + timestamp: 1677715200000, // Mar 1, 2023 + }, + }, + { + _id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0004`, + _source: { + packageId: WITHDRAWN_CHANGELOG_ITEM_ID, + id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0004`, + event: "upload-subsequent-documents", + attachments: [ + { + key: "subdoc004", + title: "Compliance Files", + filename: "compliance_documents.xlsx", + }, + ], + additionalInformation: "Compliance review files uploaded.", + timestamp: 1680307200000, // Apr 1, 2023 + }, + }, + { + _id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0005`, + _source: { + packageId: WITHDRAWN_CHANGELOG_ITEM_ID, + id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0005`, + event: "withdraw-rai", + attachments: [ + { + key: "withdraw005", + title: "Withdrawal Notice", + filename: "rai_withdrawal_notice.pdf", + }, + ], + additionalInformation: "Official notice of RAI withdrawal submitted.", + timestamp: 1682899200000, // May 1, 2023 + }, + }, + { + _id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0006`, + _source: { + packageId: WITHDRAWN_CHANGELOG_ITEM_ID, + id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0006`, + event: "withdraw-package", + attachments: [ + { + key: "withdraw006", + title: "Package Withdrawal", + filename: "package_withdrawal_request.docx", + }, + ], + additionalInformation: "Package has been withdrawn from submission pipeline.", + timestamp: 1685491200000, // Jun 1, 2023 + }, + }, + { + _id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0007`, + _source: { + packageId: WITHDRAWN_CHANGELOG_ITEM_ID, + id: `${WITHDRAWN_CHANGELOG_ITEM_ID}-0007`, + event: undefined, + attachments: [ + { + key: "misc007", + title: "Miscellaneous File", + filename: "miscellaneous_info.txt", + }, + ], + additionalInformation: "Uncategorized file upload.", + isAdminChange: false, + }, + }, + ], + }, + }, + [INITIAL_RELEASE_APPK_ITEM_ID]: { + _id: INITIAL_RELEASE_APPK_ITEM_ID, + found: true, + _source: { + id: INITIAL_RELEASE_APPK_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.PENDING, + actionType: "New", + authority: "1915(c)", + state: "MD", + origin: "OneMAC", + appkChildren: [ + { + _source: { + changedDate: "2024-01-01T00:00:00Z", + title: "Initial release", + }, + }, + ], + }, + }, +}; + +export default items; diff --git a/mocks/data/secrets.ts b/mocks/data/secrets.ts new file mode 100644 index 0000000000..badc8d4170 --- /dev/null +++ b/mocks/data/secrets.ts @@ -0,0 +1,35 @@ +import { TestSecretData } from "../index.d"; + +export const TEST_SECRET_ID = "test-secret"; // pragma: allowlist secret +export const TEST_SECRET_TO_DELETE_ID = "test-secret-to-delete"; // pragma: allowlist secret +export const TEST_SECRET_NO_VALUE_ID = "test-secret-no-value"; // pragma: allowlist secret +export const TEST_SECRET_ERROR_ID = "Throw Get Secret Error"; // pragma: allowlist secret + +const secrets: Record = { + [TEST_SECRET_ID]: { + ARN: `arn://${TEST_SECRET_ID}`, + CreatedDate: new Date("2023-01-01T12:00:00Z").getTime(), + Name: TEST_SECRET_ID, + SecretString: "test-secret-value", // pragma: allowlist secret + VersionId: "1.0", + VersionStages: ["prod"], + }, + [TEST_SECRET_TO_DELETE_ID]: { + ARN: `arn://${TEST_SECRET_TO_DELETE_ID}`, + CreatedDate: new Date("2022-01-01T12:00:00Z").getTime(), + DeletedDate: new Date("2022-12-31T23:59:59Z").getTime(), + Name: TEST_SECRET_TO_DELETE_ID, + SecretString: "deleted-test-secret-value", // pragma: allowlist secret + VersionId: "1.0", + VersionStages: ["prod"], + }, + [TEST_SECRET_NO_VALUE_ID]: { + ARN: `arn://${TEST_SECRET_NO_VALUE_ID}`, + CreatedDate: new Date("2020-03-15T12:00:00Z").getTime(), + Name: TEST_SECRET_NO_VALUE_ID, + VersionId: "1.0", + VersionStages: ["prod"], + }, +}; + +export default secrets; diff --git a/mocks/data/types.ts b/mocks/data/types.ts new file mode 100644 index 0000000000..1a6b710422 --- /dev/null +++ b/mocks/data/types.ts @@ -0,0 +1,51 @@ +export const AUTHORITY_ONE_ID = 1; +export const AUTHORITY_TWO_ID = 2; + +export const TYPE_ONE_ID = 1; +export const TYPE_TWO_ID = 2; +export const TYPE_THREE_ID = 3; +export const TYPE_FOUR_ID = 4; + +const types = [ + { + _source: { id: 101, authorityId: AUTHORITY_ONE_ID, name: "typeOne" }, + }, + { + _source: { id: 102, authorityId: AUTHORITY_ONE_ID, name: "typetwo" }, + }, + { + _source: { id: 103, authorityId: AUTHORITY_TWO_ID, name: "typethree" }, + }, + { + _source: { id: 101, authorityId: AUTHORITY_ONE_ID, name: "subtypeOne", typeId: TYPE_ONE_ID }, + }, + { + _source: { id: 102, authorityId: AUTHORITY_ONE_ID, name: "subtypetwo", typeId: TYPE_TWO_ID }, + }, + { + _source: { + id: 103, + authorityId: AUTHORITY_TWO_ID, + name: "subtypethree", + typeId: TYPE_ONE_ID, + }, + }, + { + _source: { + id: 104, + authorityId: AUTHORITY_TWO_ID, + name: "subtypethree", + typeId: TYPE_FOUR_ID, + }, + }, + { + _source: { + id: 105, + authorityId: AUTHORITY_TWO_ID, + name: "subtypethree", + typeId: TYPE_THREE_ID, + }, + }, +]; + +export default types; diff --git a/mocks/data/users/cmsReviewer.ts b/mocks/data/users/cmsReviewer.ts new file mode 100644 index 0000000000..3dd1b06fdd --- /dev/null +++ b/mocks/data/users/cmsReviewer.ts @@ -0,0 +1,139 @@ +import { TestUserData } from "../../index.d"; + +export const makoReviewer: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "mako.cmsuser@outlook.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "CMS Reviewer", + }, + { + Name: "family_name", + Value: "Cypress", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-reviewer", + }, + { + Name: "sub", + Value: "53832e35-1fbe-4c74-9111-4a0cd29ce2cf", + }, + ], + Username: "53832e35-1fbe-4c74-9111-4a0cd29ce2cf", +}; + +export const reviewer: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "reviewer@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "CMS", + }, + { + Name: "family_name", + Value: "Reviewer", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-reviewer", + }, + { + Name: "sub", + Value: "07a2519e-0bdd-4bf6-8ec0-6f88ffa684fc", + }, + ], + Username: "07a2519e-0bdd-4bf6-8ec0-6f88ffa684fc", +}; + +export const automatedReviewer: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "automated-reviewer@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "Otto", + }, + { + Name: "family_name", + Value: "Reviewer", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-reviewer", + }, + { + Name: "sub", + Value: "e04f3cc5-4cd6-4acb-9fff-210b469bc934", + }, + ], + Username: "e04f3cc5-4cd6-4acb-9fff-210b469bc934", +}; + +export const superReviewer: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "super@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "Superduper", + }, + { + Name: "family_name", + Value: "Paratrooper", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-reviewer,onemac-micro-super", + }, + { + Name: "sub", + Value: "1bddab21-ddc0-4e5b-8ee1-fe16a7883673", + }, + ], + Username: "1bddab21-ddc0-4e5b-8ee1-fe16a7883673", +}; + +export const reviewers: TestUserData[] = [makoReviewer, reviewer, automatedReviewer, superReviewer]; diff --git a/mocks/data/users/helpDeskUsers.ts b/mocks/data/users/helpDeskUsers.ts new file mode 100644 index 0000000000..255241e4c4 --- /dev/null +++ b/mocks/data/users/helpDeskUsers.ts @@ -0,0 +1,71 @@ +import { TestUserData } from "../../index.d"; + +export const helpDeskUser: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "helpdesk@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "CMS", + }, + { + Name: "family_name", + Value: "Helpdesk", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-helpdesk", + }, + { + Name: "sub", + Value: "7ebff3df-a133-4eb7-b62c-3346f2f81fd1", + }, + ], + Username: "7ebff3df-a133-4eb7-b62c-3346f2f81fd1", +}; + +export const automatedHelpDeskUser: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "automated-helpdesk@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "Otto", + }, + { + Name: "family_name", + Value: "Helpdesk", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-helpdesk", + }, + { + Name: "sub", + Value: "63d9033c-5122-48eb-a664-74d391178938", + }, + ], + Username: "63d9033c-5122-48eb-a664-74d391178938", +}; + +export const helpDeskUsers: TestUserData[] = [helpDeskUser, automatedHelpDeskUser]; diff --git a/mocks/data/users/index.ts b/mocks/data/users/index.ts new file mode 100644 index 0000000000..b84c402249 --- /dev/null +++ b/mocks/data/users/index.ts @@ -0,0 +1,49 @@ +import type { TestUserData } from "../../index.d"; +import { reviewers } from "./cmsReviewer"; +import { helpDeskUsers } from "./helpDeskUsers"; +import { readOnlyUsers } from "./readOnlyCMSUsers"; +import { stateSubmitters } from "./stateSubmitters"; + +export const noRoleUser: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "badfootball@example.com", + }, + { + Name: "given_name", + Value: "bad", + }, + { + Name: "family_name", + Value: "football", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "sub", + Value: "0d4e3b53-07c8-42c8-9a26-c7dbf7eee027", + }, + ], + Username: "0d4e3b53-07c8-42c8-9a26-c7dbf7eee027", +}; + +// return all of the possible responses +export const userResponses: TestUserData[] = [ + ...stateSubmitters, + ...reviewers, + ...helpDeskUsers, + ...readOnlyUsers, + noRoleUser, +]; + +// return an array of all usernames +export default userResponses.map((response) => ({ username: response.Username })); + +export * from "./cmsReviewer"; +export * from "./helpDeskUsers"; +export * from "./mockStorage"; +export * from "./readOnlyCMSUsers"; +export * from "./stateSubmitters"; diff --git a/mocks/data/users/mockStorage.ts b/mocks/data/users/mockStorage.ts new file mode 100644 index 0000000000..a6d412af37 --- /dev/null +++ b/mocks/data/users/mockStorage.ts @@ -0,0 +1,66 @@ +// adapted from https://github.com/mswjs/local-storage-polyfill/blob/main/src/index.ts + +export const STORAGE_MAP_SYMBOL = Symbol("map"); + +export class Storage { + private [STORAGE_MAP_SYMBOL]: Map; + + constructor() { + this[STORAGE_MAP_SYMBOL] = new Map(); + } + + /** + * Returns the number of key/value pairs. + */ + get length(): number { + return this[STORAGE_MAP_SYMBOL].size; + } + + /** + * Returns the current value associated with the given key, or null if the given key does not exist. + */ + public getItem(key: Key): string | null { + console.log(`getting item for key: ${key} value: ${this[STORAGE_MAP_SYMBOL].get(key) || null}`); + return this[STORAGE_MAP_SYMBOL].get(key) || null; + } + + /** + * Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs. + */ + public key(index: number): string | null { + console.log(`looking for key index: ${index}`); + const keys = Array.from(this[STORAGE_MAP_SYMBOL].keys()); + return keys[index] || null; + } + + /** + * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously. + * + * Unlike the browser implementation, does not throw when the value cannot be set + * because no such policy can be configured in Node.js. Does not emit the storage + * event on Window because there's no window. + */ + public setItem(key: Key, value: string): void { + console.log(`setting ${key}: ${value}`); + this[STORAGE_MAP_SYMBOL].set(key, value); + } + + /** + * Removes the key/value pair with the given key, if a key/value pair with the given key exists. + * + * Does not dispatch the storage event on Window. + */ + public removeItem(key: Key): void { + console.log(`removing item for key: ${key}`); + this[STORAGE_MAP_SYMBOL].delete(key); + } + + /** + * Removes all key/value pairs, if there are any. + * + * Does not dispatch the storage event on Window. + */ + public clear(): void { + this[STORAGE_MAP_SYMBOL].clear(); + } +} diff --git a/mocks/data/users/readOnlyCMSUsers.ts b/mocks/data/users/readOnlyCMSUsers.ts new file mode 100644 index 0000000000..ef7a7b2a15 --- /dev/null +++ b/mocks/data/users/readOnlyCMSUsers.ts @@ -0,0 +1,71 @@ +import { TestUserData } from "../../index.d"; + +export const readOnlyUser: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "readonly@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "Read", + }, + { + Name: "family_name", + Value: "Only", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-readonly", + }, + { + Name: "sub", + Value: "cd613967-c034-4e02-baad-03221840a35c", + }, + ], + Username: "cd613967-c034-4e02-baad-03221840a35c", +}; + +export const automatedReadOnlyUser: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "automated-readonly@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "Otto", + }, + { + Name: "family_name", + Value: "Readonly", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-readonly", + }, + { + Name: "sub", + Value: "acfaf0ae-1af5-4e48-ad1e-54abcee4f3bf", + }, + ], + Username: "acfaf0ae-1af5-4e48-ad1e-54abcee4f3bf", +}; + +export const readOnlyUsers: TestUserData[] = [readOnlyUser, automatedReadOnlyUser]; diff --git a/mocks/data/users/stateSubmitters.ts b/mocks/data/users/stateSubmitters.ts new file mode 100644 index 0000000000..7590f30296 --- /dev/null +++ b/mocks/data/users/stateSubmitters.ts @@ -0,0 +1,249 @@ +import { TestUserData } from "../../index.d"; + +export const makoStateSubmitter: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "mako.stateuser@gmail.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "Stateuser", + }, + { + Name: "family_name", + Value: "Tester", + }, + { + Name: "custom:state", + Value: "VA,OH,SC,CO,GA,MD", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-statesubmitter", + }, + { + Name: "sub", + Value: "cd400c39-9e7c-4341-b62f-234e2ecb339d", + }, + ], + Username: "cd400c39-9e7c-4341-b62f-234e2ecb339d", +}; + +export const stateSubmitter: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "george@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "George", + }, + { + Name: "family_name", + Value: "Harrison", + }, + { + Name: "custom:state", + Value: "VA,OH,SC,CO,GA,MD", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-statesubmitter", + }, + { + Name: "sub", + Value: "c4087448-d0e1-70c1-3d74-4f8bd1fa13fd", + }, + ], + Username: "c4087448-d0e1-70c1-3d74-4f8bd1fa13fd", +}; + +export const noDataStateSubmitter: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "nodata@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "George", + }, + { + Name: "family_name", + Value: "Harrison", + }, + { + Name: "custom:state", + Value: "VI", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-statesubmitter", + }, + { + Name: "sub", + Value: "068f3852-dd7e-484d-a423-578556f52886", + }, + ], + Username: "068f3852-dd7e-484d-a423-578556f52886", +}; + +export const coStateSubmitter: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "submitter@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "State", + }, + { + Name: "family_name", + Value: "Submitter", + }, + { + Name: "custom:state", + Value: "CO", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-statesubmitter", + }, + { + Name: "sub", + Value: "c16071b6-e24b-4405-962c-37ad6262708c", + }, + ], + Username: "c16071b6-e24b-4405-962c-37ad6262708c", +}; + +export const multiStateSubmitter: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "statemulti@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "State", + }, + { + Name: "family_name", + Value: "Multi", + }, + { + Name: "custom:state", + Value: "CA,NY,MD", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-statesubmitter", + }, + { + Name: "sub", + Value: "3de7904e-fc0a-498f-9527-8e39044edf4c", + }, + ], + Username: "3de7904e-fc0a-498f-9527-8e39044edf4c", +}; + +export const noStateSubmitter: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "statemulti@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "State", + }, + { + Name: "family_name", + Value: "Multi", + }, + { + Name: "custom:state", + Value: "", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-statesubmitter", + }, + { + Name: "sub", + Value: "604551c7-a98f-4971-aa1e-06dee6f28598", + }, + ], + Username: "604551c7-a98f-4971-aa1e-06dee6f28598", +}; + +export const automatedStateSubmitter: TestUserData = { + UserAttributes: [ + { + Name: "email", + Value: "automated-state@example.com", + }, + { + Name: "email_verified", + Value: "true", + }, + { + Name: "given_name", + Value: "Otto", + }, + { + Name: "family_name", + Value: "State", + }, + { + Name: "custom:state", + Value: "TX,CA,NY,FL", + }, + { + Name: "custom:cms-roles", + Value: "onemac-micro-statesubmitter", + }, + { + Name: "sub", + Value: "f3a1b6d6-3bc9-498d-ac22-41a6d46982c9", + }, + ], + Username: "f3a1b6d6-3bc9-498d-ac22-41a6d46982c9", +}; + +export const stateSubmitters: TestUserData[] = [ + makoStateSubmitter, + stateSubmitter, + noDataStateSubmitter, + coStateSubmitter, + multiStateSubmitter, + noStateSubmitter, + automatedStateSubmitter, +]; diff --git a/mocks/handlers/api-security.ts b/mocks/handlers/api-security.ts new file mode 100644 index 0000000000..d3b5408e90 --- /dev/null +++ b/mocks/handlers/api-security.ts @@ -0,0 +1,29 @@ +import { http, HttpResponse } from "msw"; +import { ACCESS_KEY_ID, SECRET_KEY } from "../consts"; + +const generateSessionToken = (): string | null => { + if (process.env.MOCK_USER_USERNAME) { + return Buffer.from(JSON.stringify({ username: process.env.MOCK_USER_USERNAME })).toString( + "base64", + ); + } + return null; +}; + +const defaultApiTokenHandler = http.put(/\/api\/token/, () => { + return HttpResponse.text(generateSessionToken()); +}); + +const defaultSecurityCredentialsHandler = http.get(/\/meta-data\/iam\/security-credentials/, () => { + return HttpResponse.json({ + Code: "Success", + LastUpdated: new Date().toISOString(), + Type: "AWS-HMAC", + AccessKeyId: ACCESS_KEY_ID, + SecretAccessKey: SECRET_KEY, + Token: generateSessionToken(), + Expiration: "2017-05-17T15:09:54Z", + }); +}); + +export const defaultHandlers = [defaultApiTokenHandler, defaultSecurityCredentialsHandler]; diff --git a/mocks/handlers/auth.ts b/mocks/handlers/auth.ts new file mode 100644 index 0000000000..d787a26c69 --- /dev/null +++ b/mocks/handlers/auth.ts @@ -0,0 +1,451 @@ +import { CognitoUserAttribute } from "amazon-cognito-identity-js"; +import jwt from "jsonwebtoken"; +import { http, HttpResponse, passthrough, PathParams } from "msw"; +import { APIGatewayEventRequestContext, CognitoUserAttributes } from "shared-types"; +import { isCmsUser } from "shared-utils"; +import { + ACCESS_KEY_ID, + COGNITO_IDP_DOMAIN, + IDENTITY_POOL_ID, + SECRET_KEY, + USER_POOL_CLIENT_ID, +} from "../consts"; +import { makoReviewer, makoStateSubmitter, userResponses } from "../data/users"; +import type { + IdpListUsersRequestBody, + IdpRefreshRequestBody, + IdpRequestSessionBody, + TestUserData, +} from "../index.d"; + +export const setMockUsername = (user?: TestUserData | string | null): void => { + if (user && typeof user === "string") { + process.env.MOCK_USER_USERNAME = user; + } else if (user && (user as TestUserData).Username !== undefined) { + process.env.MOCK_USER_USERNAME = (user as TestUserData).Username; + } else { + delete process.env.MOCK_USER_USERNAME; + } +}; + +export const setDefaultStateSubmitter = () => setMockUsername(makoStateSubmitter); + +export const setDefaultReviewer = () => setMockUsername(makoReviewer); + +// using any here because the function that this is mocking uses any +export const mockCurrentAuthenticatedUser = (): TestUserData | any => { + if (process.env.MOCK_USER_USERNAME) { + return findUserByUsername(process.env.MOCK_USER_USERNAME); + } + return undefined; +}; + +// using any here because the function that this is mocking uses any +export const mockUserAttributes = async (user: any): Promise => { + if (user && (user as TestUserData).UserAttributes !== undefined) { + return (user as TestUserData).UserAttributes as CognitoUserAttribute[]; + } + + if (process.env.MOCK_USER_USERNAME) { + const defaultUser = findUserByUsername(process.env.MOCK_USER_USERNAME); + return defaultUser?.UserAttributes as CognitoUserAttribute[]; + } + return {} as CognitoUserAttribute[]; +}; + +export const convertUserAttributes = (user: TestUserData): CognitoUserAttributes => { + if (user?.UserAttributes) { + const userAttributesObj = user.UserAttributes.reduce( + (obj, item) => + item?.Name && item?.Value + ? { + ...obj, + [item.Name]: item.Value, + } + : obj, + {} as CognitoUserAttributes, + ); + // Manual additions and normalizations + userAttributesObj["custom:cms-roles"] = userAttributesObj["custom:cms-roles"] || ""; + + userAttributesObj.username = user.Username || ""; + + return userAttributesObj; + } + + return {} as CognitoUserAttributes; +}; + +export const mockUseGetUser = () => { + if (process.env.MOCK_USER_USERNAME) { + const user = findUserByUsername(process.env.MOCK_USER_USERNAME); + if (user) { + // Copied from useGetUser.getUser + // Set object up with key/values from attributes array + const userAttributesObj = convertUserAttributes(user); + + return { + data: { + user: userAttributesObj, + isCms: isCmsUser(userAttributesObj), + }, + isLoading: false, + isSuccess: true, + }; + } + } + return { + data: null, + isLoading: false, + isSuccess: true, + }; +}; + +const findUserByUsername = (username: string): TestUserData | undefined => + userResponses.find((user) => user.Username == username); + +const getUsernameFromAccessToken = (accessToken?: string): string | undefined => { + if (accessToken) { + return jwt.decode(accessToken, { json: true })?.get("username"); + } + return undefined; +}; + +// const getUsernameFromSessionToken = (sessionToken?: string | null): string | undefined => { +// if (sessionToken) { +// const session = JSON.parse(Buffer.from(sessionToken, "base64").toString()); +// return session?.username; +// } +// return undefined; +// }; + +const getAttributeFromUser = (user: TestUserData, attrName: string): string | null => { + if ( + attrName && + user?.UserAttributes && + Array.isArray(user.UserAttributes) && + user.UserAttributes.length > 0 + ) { + const attribute = user.UserAttributes.find((attr) => attr?.Name == attrName); + return attribute?.Value || null; + } + return null; +}; + +const generateIdToken = (user: TestUserData, authTime: number, expTime: number): string | null => { + if (user) { + return jwt.sign( + { + sub: getAttributeFromUser(user, "sub"), + email_verified: getAttributeFromUser(user, "email_verified") === "true", + iss: COGNITO_IDP_DOMAIN, + "cognito:username": user.Username, + aud: USER_POOL_CLIENT_ID, + token_use: "id", + auth_time: authTime, + exp: expTime, + iat: authTime, + email: getAttributeFromUser(user, "email"), + }, + SECRET_KEY, + { + algorithm: "RS256", + expiresIn: "30m", + audience: USER_POOL_CLIENT_ID, + issuer: COGNITO_IDP_DOMAIN, + }, + ); + } + return null; +}; + +const generateAccessToken = ( + user: TestUserData, + authTime: number, + expTime: number, +): string | null => { + if (user) { + return jwt.sign( + { + sub: getAttributeFromUser(user, "sub"), + iss: COGNITO_IDP_DOMAIN, + version: 2, + client_id: USER_POOL_CLIENT_ID, + token_use: "access", + scope: "aws.cognito.signin.user.admin openid email", + auth_time: authTime, + exp: expTime, + iat: authTime, + username: user.Username, + }, + SECRET_KEY, + { + algorithm: "RS256", + expiresIn: "30m", + audience: USER_POOL_CLIENT_ID, + issuer: COGNITO_IDP_DOMAIN, + }, + ); + } + return null; +}; + +const generateRefreshToken = (user: TestUserData): string | null => { + if (user) { + return jwt.sign( + { + sub: getAttributeFromUser(user, "sub"), + "cognito:username": user.Username, + }, + SECRET_KEY, + { + expiresIn: "30m", + }, + ); + } + return null; +}; + +const generateSessionToken = (user: TestUserData): string | null => { + if (user?.Username) { + return Buffer.from(JSON.stringify({ username: user?.Username })).toString("base64"); + } + return null; +}; + +export const getRequestContext = (user?: TestUserData | string): APIGatewayEventRequestContext => { + let username; + if (user && typeof user === "string") { + username = user; + } else if (user && (user as TestUserData).Username !== undefined) { + username = (user as TestUserData).Username; + } else if (process.env.MOCK_USER_USERNAME) { + username = process.env.MOCK_USER_USERNAME; + } + + if (username) { + return { + identity: { + cognitoAuthenticationProvider: `${COGNITO_IDP_DOMAIN},${COGNITO_IDP_DOMAIN}:CognitoSignIn:${username}`, + }, + } as APIGatewayEventRequestContext; + } + return { + identity: {}, + } as APIGatewayEventRequestContext; +}; + +export const signInHandler = http.post(/amazoncognito.com\/oauth2\/token/, async () => { + if (process.env.MOCK_USER_USERNAME) { + const user = findUserByUsername(process.env.MOCK_USER_USERNAME); + if (user) { + const authTime = Date.now() / 1000; + const expTime = authTime + 1800; + return HttpResponse.json({ + id_token: generateIdToken(user, authTime, expTime), + access_token: generateAccessToken(user, authTime, expTime), + refresh_token: generateRefreshToken(user), + expires_in: 1800, + token_type: "Bearer", + }); + } + return new HttpResponse("No user found with this sub", { status: 404 }); + } + return new HttpResponse("User not set", { status: 401 }); +}); + +export const identityServiceHandler = http.post( + /cognito-identity/, + async ({ request }) => { + const target = request.headers.get("x-amz-target"); + if (target) { + if (target == "AWSCognitoIdentityService.GetId") { + const responseBuffer = Buffer.from(JSON.stringify({ IdentityId: IDENTITY_POOL_ID })); + const responseEncoded = responseBuffer.toString("base64"); + + return new HttpResponse(responseEncoded, { + headers: { + "Content-Type": "application/x-amz-json-1.1", + "Content-Encoding": "base64", + "Content-Length": responseBuffer.byteLength.toString(), + }, + }); + } + + if (target == "AWSCognitoIdentityService.GetCredentialsForIdentity") { + let username; + const { Logins } = await request.json(); + if (Logins?.value) { + const payload = jwt.decode(Logins.value); + if (payload && typeof payload !== "string") { + username = payload["cognito:username"].toString(); + } + } + if (!username) { + username = process.env.MOCK_USER_USERNAME; + } + + if (username) { + const user = findUserByUsername(username); + + if (user) { + const responseBuffer = Buffer.from( + JSON.stringify({ + Credentials: { + AccessKeyId: ACCESS_KEY_ID, + Expiration: 1.73, + SecretKey: SECRET_KEY, + SessionToken: generateSessionToken(user), + }, + IdentityId: IDENTITY_POOL_ID, + }), + ); + + const responseEncoded = responseBuffer.toString("base64"); + + return new HttpResponse(responseEncoded, { + headers: { + "Content-Type": "application/x-amz-json-1.1", + "Content-Encoding": "base64", + "Content-Length": responseBuffer.byteLength.toString(), + }, + }); + } + + return new HttpResponse("No user found with this sub", { status: 404 }); + } + return new HttpResponse("User not set", { status: 401 }); + } + console.error(`x-amz-target ${target} not mocked`); + return passthrough(); + } + console.error("x-amz-target not present"); + return passthrough(); + }, +); + +export const identityProviderServiceHandler = http.post< + PathParams, + IdpRequestSessionBody | IdpRefreshRequestBody | IdpListUsersRequestBody +>(/https:\/\/cognito-idp.\S*.amazonaws.com\//, async ({ request }) => { + const target = request.headers.get("x-amz-target"); + if (target) { + if (target == "AWSCognitoIdentityProviderService.InitiateAuth") { + const { AuthFlow, AuthParameters } = (await request.json()) as IdpRefreshRequestBody; + if (AuthFlow === "REFRESH_TOKEN_AUTH" && AuthParameters?.REFRESH_TOKEN) { + const payload = jwt.decode(AuthParameters.REFRESH_TOKEN); + let username; + if (payload && typeof payload !== "string") { + username = payload["cognito:username"].toString(); + } + if (!username) { + username = process.env.MOCK_USER_USERNAME; + } + + if (username) { + const user = findUserByUsername(username); + if (user) { + const authTime = Date.now() / 1000; + const expTime = authTime + 1800; + return HttpResponse.json({ + AuthenticationResult: { + AccessToken: generateAccessToken(user, authTime, expTime), + ExpiresIn: 1800, + IdToken: generateIdToken(user, authTime, expTime), + NewDeviceMetadata: { + DeviceGroupKey: "string3", + DeviceKey: "string4", + }, + RefreshToken: generateRefreshToken(user), + TokenType: "Bearer", + }, + AvailableChallenges: ["string7"], + ChallengeName: "string8", + ChallengeParameters: { + string: "string9", + }, + Session: generateSessionToken(user), + }); + } + return new HttpResponse("No user found with this sub", { status: 404 }); + } + return new HttpResponse("User not set", { status: 401 }); + } + console.error( + `Authflow ${AuthFlow} or AuthParameters ${JSON.stringify(AuthParameters)} not supported`, + ); + return passthrough(); + } + + if (target == "AWSCognitoIdentityProviderService.GetUser") { + const { AccessToken } = (await request.json()) as IdpRequestSessionBody; + const username = getUsernameFromAccessToken(AccessToken) || process.env.MOCK_USER_USERNAME; + + // const agent = request.headers.get("x-amz-user-agent"); + + // if (agent == "aws-amplify/5.0.4 auth framework/0") { + // called by Auth.currentAuthenticatedUser + + // } else if (agent == "aws-amplify/5.0.4 auth framework/1") { + // called by Auth.userAttributes + + if (username) { + const user = findUserByUsername(username); + if (user) { + const encodedUser = Buffer.from(JSON.stringify(user)).toString("base64"); + + return new HttpResponse(encodedUser, { + headers: { + "Content-Type": "application/x-amz-json-1.1", + "Content-Encoding": "base64", + }, + }); + } + return new HttpResponse("No user found with this sub", { status: 404 }); + } + return new HttpResponse("User not set", { status: 401 }); + } + + if (target == "AWSCognitoIdentityProviderService.ListUsers") { + const { Filter } = (await request.json()) as IdpListUsersRequestBody; + const username = + Filter.replace("sub = ", "").replaceAll('"', "") || process.env.MOCK_USER_USERNAME; + + if (username) { + const user = findUserByUsername(username); + if (user) { + return HttpResponse.json({ + Users: [ + { + Attributes: user.UserAttributes, + Username: user.Username, + UserStatus: "CONFIRMED", + Enabled: true, + }, + ], + }); + } + } + return HttpResponse.json({ + Users: [], + }); + } + + console.error(`x-amz-target ${target} not mocked`); + return passthrough(); + } + + console.error("x-amz-target not present"); + return passthrough(); +}); + +export type IdentityRequest = { + IdentityPoolId: string; + Logins: Record; +}; + +export const defaultHandlers = [ + signInHandler, + identityProviderServiceHandler, + identityServiceHandler, +]; diff --git a/mocks/handlers/cloudformation.ts b/mocks/handlers/cloudformation.ts new file mode 100644 index 0000000000..9a9494834a --- /dev/null +++ b/mocks/handlers/cloudformation.ts @@ -0,0 +1,54 @@ +import { http, HttpResponse } from "msw"; +import exports from "../data/cloudFormationsExports"; + +export const errorCloudFormation = http.post( + `https://cloudformation.us-east-1.amazonaws.com/`, + async () => + HttpResponse.xml( + ` + + ServiceUnavailable + Service is unable to handle request. + + `, + { + status: 503, + statusText: "ServiceUnavailable", + }, + ), +); + +const defaultCloudFormation = http.post( + `https://cloudformation.us-east-1.amazonaws.com/`, + async () => { + let xmlResponse = ` + + + + `; + + xmlResponse += exports + .map( + (val) => ` + + ${val.Name} + ${val.ExportingStackId} + ${val.Value} + + `, + ) + .join(); + + xmlResponse += ` + + + + 5ccc7dcd-744c-11e5-be70-1b08c228efb3 + +`; + + return HttpResponse.xml(xmlResponse); + }, +); + +export const defaultHandlers = [defaultCloudFormation]; diff --git a/mocks/handlers/counties.ts b/mocks/handlers/counties.ts new file mode 100644 index 0000000000..3387184443 --- /dev/null +++ b/mocks/handlers/counties.ts @@ -0,0 +1,29 @@ +import { http, HttpResponse } from "msw"; +import counties from "../data/counties"; + +export type PopulationParams = { + get: string | string[]; + for: string | string[]; + in: string | string[]; +}; + +const defaultCountiesHandler = http.get( + "https://api.census.gov/data/2019/pep/population", + async ({ request }) => { + const requestUrl = new URL(request.url); + const stateSearchParam = requestUrl.searchParams.get("in") || ""; + const stateParamStringArray = stateSearchParam.split(":"); + const stateCode = + stateParamStringArray && + Array.isArray(stateParamStringArray) && + stateParamStringArray.length > 1 + ? stateParamStringArray[1] + : null; + const countyListForState = stateCode ? counties[stateCode] : null; + return countyListForState + ? HttpResponse.json(countyListForState) + : new HttpResponse(null, { status: 404 }); + }, +); + +export const defaultHandlers = [defaultCountiesHandler]; diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts new file mode 100644 index 0000000000..f2328daccc --- /dev/null +++ b/mocks/handlers/index.ts @@ -0,0 +1,54 @@ +import { http, HttpResponse } from "msw"; +import { defaultHandlers as apiTokenHandlers } from "./api-security.js"; +import { defaultHandlers as authHandlers } from "./auth.js"; +import { defaultHandlers as cloudFormationHandlers } from "./cloudformation.js"; +import { defaultHandlers as countiesHandler } from "./counties.js"; +import { defaultHandlers as itemHandlers } from "./items.js"; +import { defaultHandlers as searchHandlers } from "./opensearch.js"; +import { defaultHandlers as secretsManagerHandlers } from "./secretsmanager.js"; +import { defaultHandlers as submissionHandlers } from "./submissions.js"; +import { defaultHandlers as typeHandlers } from "./types.js"; + +export type Body = + | Blob + | ArrayBuffer + | FormData + | ReadableStream + | URLSearchParams + | string + | null + | undefined; + +export const postOnceHandler = (endpoint: string, status: number = 200, body?: Body) => + http.post( + endpoint, + async () => { + return new HttpResponse(body, { status }); + }, + { once: true }, + ); + +export default [ + ...itemHandlers, + ...typeHandlers, + ...submissionHandlers, + ...countiesHandler, + ...authHandlers, + ...searchHandlers, + ...secretsManagerHandlers, + ...cloudFormationHandlers, + ...apiTokenHandlers, +]; + +export { + convertUserAttributes, + getRequestContext, + mockCurrentAuthenticatedUser, + mockUseGetUser, + mockUserAttributes, + setDefaultReviewer, + setDefaultStateSubmitter, + setMockUsername, +} from "./auth.js"; + +export { errorCloudFormation } from "./cloudformation.js"; diff --git a/mocks/handlers/items.ts b/mocks/handlers/items.ts new file mode 100644 index 0000000000..bc8ef5e035 --- /dev/null +++ b/mocks/handlers/items.ts @@ -0,0 +1,28 @@ +import { http, HttpResponse } from "msw"; +import items, { GET_ERROR_ITEM_ID } from "../data/items"; +import { GetItemBody } from "../index.d"; + +const defaultItemHandler = http.post(/\/item$/, async ({ request }) => { + const { id } = await request.json(); + + if (id == GET_ERROR_ITEM_ID) { + return new HttpResponse("Internal server error", { status: 500 }); + } + + const item = items[id] || null; + + return item ? HttpResponse.json(item) : new HttpResponse(null, { status: 404 }); +}); + +const defaultItemExistsHandler = http.post( + /\/itemExists$/, + async ({ request }) => { + const { id } = await request.json(); + if (id == GET_ERROR_ITEM_ID) { + return new HttpResponse("Internal server error", { status: 500 }); + } + return HttpResponse.json({ exists: !!items[id]?._source }); + }, +); + +export const defaultHandlers = [defaultItemHandler, defaultItemExistsHandler]; diff --git a/mocks/handlers/opensearch.ts b/mocks/handlers/opensearch.ts new file mode 100644 index 0000000000..ec2311b099 --- /dev/null +++ b/mocks/handlers/opensearch.ts @@ -0,0 +1,301 @@ +import { http, HttpResponse, PathParams } from "msw"; +import { GET_ERROR_ITEM_ID } from "../data"; +import items from "../data/items"; +import { + SearchQueryBody, + SearchTerm, + TestAppkDocument, + TestAppkItemResult, + TestChangelogDocument, + TestChangelogItemResult, + TestItemResult, + TestMainDocument, +} from "../index.d"; + +const defaultMainDocumentHandler = http.get( + `https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-main/_doc/:id`, + async ({ params }) => { + const { id } = params; + if (id == GET_ERROR_ITEM_ID) { + return new HttpResponse("Internal server error", { status: 500 }); + } + const itemId = id && Array.isArray(id) ? id[0] : id; + const item = items[itemId] || null; + + return item ? HttpResponse.json(item) : new HttpResponse(null, { status: 404 }); + }, +); + +const getFilterValue = ( + query: SearchTerm | SearchTerm[], + filterName: string, +): string | string[] | undefined => { + if (query) { + const rule: SearchTerm | undefined = Array.isArray(query) + ? (query as SearchTerm[]).find( + (rule) => + rule?.term?.[filterName] !== undefined || rule?.terms?.[filterName] !== undefined, + ) + : (query as SearchTerm); + + if (rule?.term?.[filterName]) { + return rule.term[filterName].toString(); + } + + if (rule?.terms?.[filterName]) { + return rule.terms[filterName].map((value: string) => value?.toString()); + } + } + return; +}; + +const getFilterKeys = (query: SearchTerm[] | SearchTerm): string[] => { + const filterKeys: string[] = []; + if (query) { + if (Array.isArray(query)) { + (query as SearchTerm[]).forEach((rule) => { + if (rule?.term !== undefined) { + filterKeys.push(...Object.keys(rule.term)); + } + if (rule?.terms !== undefined) { + filterKeys.push(...Object.keys(rule.terms)); + } + }); + } else { + if ((query as SearchTerm)?.term !== undefined) { + filterKeys.push(...Object.keys((query as SearchTerm).term)); + } + if ((query as SearchTerm)?.terms !== undefined) { + filterKeys.push(...Object.keys((query as SearchTerm).terms)); + } + } + } + return filterKeys; +}; + +function matchFilter( + item: T | null | undefined, + filterTerm: keyof T | null | undefined, + filterValue: string | string[] | null | undefined, +): boolean { + if (!item || !filterTerm || !filterValue) { + return false; + } + + const itemValue = item?.[filterTerm]?.toString()?.toLocaleLowerCase() || ""; + + if (Array.isArray(filterValue)) { + return filterValue.map((value) => value?.toString().toLocaleLowerCase()).includes(itemValue); + } + + return filterValue?.toString()?.toLocaleLowerCase() == itemValue; +} + +const filterItemResultByTerm = ( + hits: TestItemResult[], + filterTerm: keyof TestMainDocument, + filterValue: string | string[], +): TestItemResult[] => { + return hits.filter( + (hit) => hit?._source && matchFilter(hit._source, filterTerm, filterValue), + ); +}; + +const filterAppkChildrenByTerm = ( + hits: TestAppkItemResult[], + filterTerm: keyof TestAppkDocument, + filterValue: string | string[], +): TestAppkItemResult[] => { + return hits.filter( + (hit) => + hit?._source && + matchFilter(hit._source as TestAppkDocument, filterTerm, filterValue), + ); +}; + +const filterChangelogByTerm = ( + hits: TestChangelogItemResult[], + filterTerm: keyof TestChangelogDocument, + filterValue: string | string[], +): TestChangelogItemResult[] => { + return hits.filter( + (hit) => + hit?._source && + matchFilter( + hit._source as TestChangelogDocument, + filterTerm, + filterValue, + ), + ); +}; + +const defaultMainSearchHandler = http.post( + "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-main/_search", + async ({ request }) => { + const { query } = await request.json(); + + if (query?.match_all == "throw-error") { + return new HttpResponse("Internal server error", { status: 500 }); + } + + const must = query?.bool?.must; + const mustTerms = must ? getFilterKeys(must) : []; + + // check if searching for appkChildren + if (mustTerms.includes("appkParentId.keyword") || mustTerms.includes("appkParentId")) { + const appkParentIdValue = + getFilterValue(must, "appkParentId.keyword") || getFilterValue(must, "appkParentId"); + + const appkParentId = + Array.isArray(appkParentIdValue) && appkParentIdValue.length > 0 + ? appkParentIdValue[0]?.toString() + : appkParentIdValue?.toString(); + + if (appkParentId == GET_ERROR_ITEM_ID) { + return new HttpResponse("Internal server error", { status: 500 }); + } + + const item = items[appkParentId || ""] || null; + + if (item?._source) { + let appkChildren: TestAppkItemResult[] = + (item._source?.appkChildren as TestAppkItemResult[]) || []; + if (appkChildren.length > 0) { + mustTerms.forEach((term) => { + const filterValue = getFilterValue(must, term); + const filterTerm: keyof TestAppkDocument = term.replace( + ".keyword", + "", + ) as keyof TestAppkDocument; + if (filterValue) { + appkChildren = filterAppkChildrenByTerm(appkChildren, filterTerm, filterValue); + } + }); + } + + return HttpResponse.json({ + took: 5, + timed_out: false, + _shards: { + total: 5, + successful: 5, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: appkChildren.length, + relation: "eq", + }, + max_score: null, + hits: appkChildren, + }, + }); + } + } + + let itemHits: TestItemResult[] = Object.values(items) || []; + + if (itemHits.length > 0) { + mustTerms.forEach((term) => { + const filterValue = getFilterValue(must, term); + const filterTerm: keyof TestMainDocument = term.replace( + ".keyword", + "", + ) as keyof TestMainDocument; + if (filterValue) { + itemHits = filterItemResultByTerm(itemHits, filterTerm, filterValue); + } + }); + } + + return HttpResponse.json({ + took: 5, + timed_out: false, + _shards: { + total: 5, + successful: 5, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: itemHits.length, + relation: "eq", + }, + max_score: null, + hits: itemHits, + }, + }); + }, +); + +const defaultChangelogSearchHandler = http.post( + "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-changelog/_search", + async ({ request }) => { + const { query } = await request.json(); + const must = query?.bool?.must; + const mustTerms = must ? getFilterKeys(must) : []; + + const packageIdValue = getFilterValue(must, "packageId.keyword"); + + if (!packageIdValue) { + return new HttpResponse("No packageId provided", { status: 400 }); + } + + const packageId = + Array.isArray(packageIdValue) && packageIdValue.length > 0 + ? packageIdValue[0]?.toString() + : packageIdValue?.toString(); + + if (packageId == GET_ERROR_ITEM_ID) { + return new HttpResponse("Internal server error", { status: 500 }); + } + + const item = items[packageId] || null; + + if (item?._source) { + let changelog: TestChangelogItemResult[] = + (item._source?.changelog as TestChangelogItemResult[]) || []; + if (changelog.length > 0) { + mustTerms.forEach((term) => { + const filterValue = getFilterValue(must, term); + const filterTerm: keyof TestChangelogDocument = term.replace( + ".keyword", + "", + ) as keyof TestChangelogDocument; + if (filterValue) { + changelog = filterChangelogByTerm(changelog, filterTerm, filterValue); + } + }); + } + + return HttpResponse.json({ + took: 5, + timed_out: false, + _shards: { + total: 5, + successful: 5, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: changelog.length, + relation: "eq", + }, + max_score: null, + hits: changelog, + }, + }); + } + + return new HttpResponse(null, { status: 404 }); + }, +); + +export const defaultHandlers = [ + defaultMainDocumentHandler, + defaultMainSearchHandler, + defaultChangelogSearchHandler, +]; diff --git a/mocks/handlers/secretsmanager.ts b/mocks/handlers/secretsmanager.ts new file mode 100644 index 0000000000..4bf7d34cb6 --- /dev/null +++ b/mocks/handlers/secretsmanager.ts @@ -0,0 +1,53 @@ +import { http, HttpResponse, PathParams } from "msw"; +import type { SecretManagerRequestBody } from ".."; +import secrets, { TEST_SECRET_ERROR_ID } from "../data/secrets"; + +const defaultSecretHandler = http.post( + `https://secretsmanager.us-east-1.amazonaws.com`, + async ({ request }) => { + const { SecretId } = await request.json(); + if (!SecretId) { + return HttpResponse.json({ + name: "InvalidParameterException", + message: "Missing SecretId in request body", + status: 400, + }); + } + + if (SecretId == TEST_SECRET_ERROR_ID) { + return HttpResponse.json({ + name: "InternalServiceError", + message: "Internal service error", + status: 500, + }); + } + + const secret = secrets[SecretId]; + + if (secret) { + const target = request.headers.get("x-amz-target"); + if (target == "secretsmanager.DescribeSecret") { + return HttpResponse.json({ + ...secret, + }); + } + if (target == "secretsmanager.GetSecretValue") { + return HttpResponse.json(secret); + } + + console.log(`target: ${target} not supported`); + return HttpResponse.json({ + name: "ResourceNotFoundException", + message: "Target not supported", + status: 500, + }); + } + return HttpResponse.json({ + name: "ResourceNotFoundException", + message: "No secret found for Id", + status: 404, + }); + }, +); + +export const defaultHandlers = [defaultSecretHandler]; diff --git a/mocks/handlers/submissions.ts b/mocks/handlers/submissions.ts new file mode 100644 index 0000000000..5548ff414e --- /dev/null +++ b/mocks/handlers/submissions.ts @@ -0,0 +1,44 @@ +import { http, HttpResponse } from "msw"; +import { SUBMISSION_ERROR_ITEM_ID } from "../data/items"; + +export type SubmitRequestBody = { id: string }; + +const defaultUploadHandler = http.put( + /\/upload/, + async () => new HttpResponse(null, { status: 200 }), +); + +const defaultUploadUrlHandler = http.post(/\/getUploadUrl/, () => + HttpResponse.json( + { + url: "/upload", + key: "test-key", + bucket: "test-bucket", + }, + { status: 200 }, + ), +); + +// const defaultTestHandler = http.post(/\/test$/, () => +// HttpResponse.json({ message: "pass" }, { status: 200 }), +// ); + +const defaultSubmitHandler = http.post( + /\/submit$/, + async ({ request }) => { + const { id } = await request.json(); + + if (id == SUBMISSION_ERROR_ITEM_ID) { + return new HttpResponse("Internal server error", { status: 500 }); + } + + return HttpResponse.json({ message: "success" }, { status: 200 }); + }, +); + +export const defaultHandlers = [ + defaultUploadHandler, + defaultUploadUrlHandler, + // defaultTestHandler, + defaultSubmitHandler, +]; diff --git a/mocks/handlers/types.ts b/mocks/handlers/types.ts new file mode 100644 index 0000000000..4c16d4e390 --- /dev/null +++ b/mocks/handlers/types.ts @@ -0,0 +1,46 @@ +import { http, HttpResponse } from "msw"; +import types from "../data/types"; + +type GetTypesBody = { authorityId: number }; +type GetSubTypesBody = { authorityId: number; typeIds: number[] }; + +const defaultTypeHandler = http.post(/\/getTypes$/, async ({ request }) => { + const { authorityId } = await request.json(); + + if (authorityId === -1) { + throw Error("useGetTypes > mockFetch: Expected error thrown by test."); + } + + return HttpResponse.json({ + hits: { + hits: types.filter( + (item) => item._source.authorityId === authorityId && !item._source.typeId, + ), + }, + }); +}); + +const defaultSubTypesHandler = http.post( + /\/getSubTypes$/, + async ({ request }) => { + const { authorityId, typeIds } = await request.json(); + + if (authorityId === -1) { + throw Error("useGetSubTypes > mockFetch: Expected error thrown by test."); + } + + const filteredData = types.filter( + (item) => + item._source.authorityId === authorityId && + typeIds.some((typeId) => item._source.typeId === typeId), + ); + + return HttpResponse.json({ + hits: { + hits: filteredData, + }, + }); + }, +); + +export const defaultHandlers = [defaultTypeHandler, defaultSubTypesHandler]; diff --git a/mocks/index.d.ts b/mocks/index.d.ts new file mode 100644 index 0000000000..1619e7d9cf --- /dev/null +++ b/mocks/index.d.ts @@ -0,0 +1,63 @@ +import type { Common, Search_RequestBody, TermQuery } from "@opensearch-project/opensearch"; +import type { APIGatewayEventRequestContext, UserData, opensearch } from "shared-types"; + +import type { Export } from "@aws-sdk/client-cloudformation"; +import type { GetSecretValueCommandOutput } from "@aws-sdk/client-secrets-manager"; + +// code borrowed from https://stackoverflow.com/questions/47914536/use-partial-in-nested-property-with-typescript +export type DeepPartial = { + [P in keyof T]?: DeepPartial; +}; + +export type TestUserData = DeepPartial; + +export type TestItemResult = DeepPartial; + +export type TestMainDocument = TestItemResult["_source"]; + +export type TestAppkItemResult = Omit; + +export type TestAppkDocument = TestAppkItemResult["_source"]; + +export type TestChangelogItemResult = DeepPartial; + +export type TestChangelogDocument = TestChangelogItemResult["_source"]; + +export type TestSecretData = Partial> & { + CreatedDate: number; + DeletedDate?: number; +}; + +export type TestExport = Partial; + +export type IdpRequestSessionBody = { + AccessToken: string; +}; + +export type IdpRefreshRequestBody = { + ClientId: string; + AuthFlow: "REFRESH_TOKEN_AUTH"; + AuthParameters: { + REFRESH_TOKEN: string; + DEVICE_KEY: null; + }; +}; + +export type IdpListUsersRequestBody = { + UserPoolId: string; + Filter: string; +}; + +export type SecretManagerRequestBody = { + SecretId: string; +}; + +export type GetItemBody = { id: string }; + +export type SearchQueryBody = Search_RequestBody; + +export type SearchTerm = Record; + +export type EventRequestContext = Partial; + +export type TestCounty = [string, string, string]; diff --git a/mocks/index.ts b/mocks/index.ts new file mode 100644 index 0000000000..add75df43b --- /dev/null +++ b/mocks/index.ts @@ -0,0 +1,8 @@ +export * from "./consts"; +export * from "./data"; +export * from "./handlers"; +export * from "./index.d"; + +export { http, HttpResponse } from "msw"; + +export const MOCK_API_URL = "https://test-domain.execute-api.us-east-1.amazonaws.com/mocked"; diff --git a/mocks/package.json b/mocks/package.json new file mode 100644 index 0000000000..c7054dc32f --- /dev/null +++ b/mocks/package.json @@ -0,0 +1,24 @@ +{ + "name": "mocks", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "", + "main": "index.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@opensearch-project/opensearch": "^2.3.0", + "@types/jsonwebtoken": "9.0.7", + "amazon-cognito-identity-js": "6.3.12", + "aws-lambda": "1.0.7", + "jsonwebtoken": "9.0.2", + "msw": "^2.3.1", + "react-app": "*", + "shared-types": "*", + "shared-utils": "*" + }, + "author": "", + "license": "ISC" +} diff --git a/mocks/server.ts b/mocks/server.ts new file mode 100644 index 0000000000..f764c386e3 --- /dev/null +++ b/mocks/server.ts @@ -0,0 +1,4 @@ +import { setupServer } from "msw/node"; +import handlers from "./handlers"; + +export const mockedServer = setupServer(...handlers); diff --git a/mocks/tsconfig.json b/mocks/tsconfig.json new file mode 100644 index 0000000000..893af15986 --- /dev/null +++ b/mocks/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "baseUrl": ".", + "declaration": true + }, + "include": ["."], + "exclude": ["node_modules"] +} diff --git a/package.json b/package.json index 262b8a503a..7459fedf94 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "lib/packages/*", "react-app", "bin/cli", + "mocks", "test/e2e" ], "license": "CC0-1.0", diff --git a/react-app/package.json b/react-app/package.json index b830968153..aad3ca2e72 100644 --- a/react-app/package.json +++ b/react-app/package.json @@ -13,18 +13,9 @@ }, "dependencies": { "@aws-amplify/auth": "^5.4.0", - "@emotion/react": "^11.11.1", - "@emotion/styled": "^11.11.0", "@fontsource/open-sans": "^5.0.17", "@heroicons/react": "^2.0.17", - "@hookform/error-message": "^2.0.1", "@hookform/resolvers": "^3.3.2", - "@mui/lab": "^5.0.0-alpha.136", - "@mui/material": "^5.14.1", - "@mui/styled-engine": "^5.13.2", - "@mui/styles": "^5.14.0", - "@mui/system": "^5.14.1", - "@mui/x-data-grid": "^6.10.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", @@ -41,21 +32,17 @@ "@radix-ui/react-tooltip": "^1.1.2", "@tanstack/react-query": "^4.29.1", "@tanstack/react-query-devtools": "^4.29.5", - "@types/file-saver": "^2.0.5", - "ajv-errors": "^3.0.0", "aws-amplify": "^5.3.12", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "date-fns": "^2.30.0", "export-to-csv": "^0.2.1", "file-saver": "^2.0.5", - "flat": "^6.0.1", "framer-motion": "^10.16.1", "jszip": "^3.10.1", "launchdarkly-react-client-sdk": "^3.0.10", "lucide-react": "^0.291.0", "lz-string": "^1.5.0", - "moment-timezone": "^0.5.44", "pluralize": "^8.0.0", "react": "^18.2.0", "react-day-picker": "^8.8.1", @@ -63,12 +50,11 @@ "react-dropzone": "^14.2.3", "react-hook-form": "^7.46.2", "react-loader-spinner": "^6.1.6", - "react-router-dom": "^6.10.0", + "react-router": "^7.0.2", "react-select": "^5.7.4", "shared-types": "*", "shared-utils": "*", "tailwind-merge": "^1.14.0", - "tailwindcss-animate": "^1.0.7", "uuid": "^9.0.0", "zod": "^3.22.3" }, diff --git a/react-app/src/api/amplifyConfig.ts b/react-app/src/api/amplifyConfig.ts index 42717ef95b..30eee09ed4 100644 --- a/react-app/src/api/amplifyConfig.ts +++ b/react-app/src/api/amplifyConfig.ts @@ -1,5 +1,5 @@ -import { Amplify } from "aws-amplify"; import config from "@/config"; +import { Amplify } from "aws-amplify"; Amplify.configure({ Auth: { diff --git a/react-app/src/api/index.ts b/react-app/src/api/index.ts index dd4bd83b8f..ae1793744a 100644 --- a/react-app/src/api/index.ts +++ b/react-app/src/api/index.ts @@ -7,6 +7,5 @@ export * from "./useGetPackageActions"; export * from "./useGetCPOCs"; export * from "./useGetTypes"; export * from "./amplifyConfig"; -export * from "./submissionService"; export * from "./itemExists"; export * from "./useGetCounties"; diff --git a/react-app/src/api/itemExists.ts b/react-app/src/api/itemExists.ts index 6f9ba800b8..b3ebb72021 100644 --- a/react-app/src/api/itemExists.ts +++ b/react-app/src/api/itemExists.ts @@ -2,6 +2,5 @@ import { API } from "aws-amplify"; export const itemExists = async (id: string): Promise => { const response = await API.post("os", "/itemExists", { body: { id } }); - console.log(response); return response.exists; }; diff --git a/react-app/src/api/mocks/index.ts b/react-app/src/api/mocks/index.ts deleted file mode 100644 index 08cbffb832..0000000000 --- a/react-app/src/api/mocks/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * as mockItem from "./item"; -export * as mockSubmit from "./submit"; -export * as mockTypes from "./types"; diff --git a/react-app/src/api/mocks/item.ts b/react-app/src/api/mocks/item.ts deleted file mode 100644 index b45cfe5eee..0000000000 --- a/react-app/src/api/mocks/item.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; -import { opensearch, SEATOOL_STATUS } from "shared-types"; - -type GetItemBody = { id: string }; -type ItemTestFields = Pick< - opensearch.main.Document, - "id" | "seatoolStatus" | "actionType" ->; - -const ID_SEPARATOR = "-"; -type IdParamKey = keyof opensearch.main.Document; -// Because getItem(id: string) doesn't allow for easy object mocking, -// to make tests easier, you can add params to the ids your tests use -// and mock specific attributes. -// e.x. existing-approved-actionType=New -const getIdParam = (id: string, key: IdParamKey) => - id - .split(ID_SEPARATOR) - .find((param: string) => param.includes(key)) - ?.slice(key.length + 1); // + 1 to cover the `=` sign - -const handlers = [ - http.post( - "/item-mock-server", - async ({ request }) => { - const { id } = await request.json(); - return id.includes("existing") - ? HttpResponse.json({ - _id: id, - _source: { - id: id, - seatoolStatus: id.includes("approved") - ? SEATOOL_STATUS.APPROVED - : SEATOOL_STATUS.PENDING, - actionType: getIdParam(id, "actionType") || "New", - } satisfies ItemTestFields, - }) - : new HttpResponse(null, { status: 404 }); - }, - ), -]; - -export const server = setupServer(...handlers); diff --git a/react-app/src/api/mocks/submit.ts b/react-app/src/api/mocks/submit.ts deleted file mode 100644 index d16934ccf0..0000000000 --- a/react-app/src/api/mocks/submit.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; - -const handlers = [ - http.put("/upload", async () => { - return new HttpResponse(null, { status: 200 }); - }), - http.post("/getUploadUrl", () => { - return HttpResponse.json( - { - url: "/upload", - key: "test-key", - bucket: "test-bucket", - }, - { status: 200 }, - ); - }), - http.post("/test", () => { - return HttpResponse.json({ message: "pass" }, { status: 200 }); - }), -]; - -export const server = setupServer(...handlers); diff --git a/react-app/src/api/mocks/types.ts b/react-app/src/api/mocks/types.ts deleted file mode 100644 index 07a5a26aac..0000000000 --- a/react-app/src/api/mocks/types.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; - -type GetTypesBody = { authorityId: number }; -type GetSubTypesBody = { authorityId: number; typeIds: number[] }; - -export const handlers = [ - http.post("/os/getTypes", async ({ request }) => { - const { authorityId } = await request.json(); - - if (authorityId === -1) { - throw Error("useGetTypes > mockFetch: Expected error thrown by test."); - } - - return HttpResponse.json({ - hits: { - hits: mockData.filter( - (item) => - item._source.authorityId === authorityId && !item._source.typeId, - ), - }, - }); - }), - - http.post("/os/getSubTypes", async ({ request }) => { - const { authorityId, typeIds } = await request.json(); - - if (authorityId === -1) { - throw Error("useGetSubTypes > mockFetch: Expected error thrown by test."); - } - - const filteredData = mockData.filter( - (item) => - item._source.authorityId === authorityId && - typeIds.some((typeId) => item._source.typeId === typeId), - ); - - return HttpResponse.json({ - hits: { - hits: filteredData, - }, - }); - }), -]; - -const mockData = [ - { _source: { id: 101, authorityId: 1, name: "typeOne" } }, - { _source: { id: 102, authorityId: 1, name: "typetwo" } }, - { _source: { id: 103, authorityId: 2, name: "typethree" } }, - { _source: { id: 101, authorityId: 1, name: "subtypeOne", typeId: 1 } }, - { _source: { id: 102, authorityId: 1, name: "subtypetwo", typeId: 2 } }, - { _source: { id: 103, authorityId: 2, name: "subtypethree", typeId: 1 } }, - { _source: { id: 104, authorityId: 2, name: "subtypethree", typeId: 4 } }, - { _source: { id: 105, authorityId: 2, name: "subtypethree", typeId: 3 } }, -]; - -export const server = setupServer(...handlers); diff --git a/react-app/src/api/submissionService.test.ts b/react-app/src/api/submissionService.test.ts deleted file mode 100644 index f515eec7a9..0000000000 --- a/react-app/src/api/submissionService.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { describe, expect, it } from "vitest"; -import * as unit from "./submissionService"; -import { OneMacUser } from "@/api/useGetUser"; -import { SubmissionServiceEndpoint } from "@/utils"; -import { Authority } from "shared-types"; - -const mockFormData = { - test: "data", - proposedEffectiveDate: new Date(), - id: "MD-12-3456", - attachments: { - test: [new File([""], "test.pdf")], - }, -}; - -const mockUploadRecipes = (n: number) => { - const res: unit.UploadRecipe[] = []; - while (n > 0) { - res.push({ - key: `test-${n}`, - url: `/upload/${n}`, - bucket: `bucket-${n}`, - data: new File([""], `test-${n}.pdf`), - title: "other", - name: `name-${n}`, - }); - n--; - } - return res; -}; - -const mockGeorge: OneMacUser = { - isCms: false, - user: { - sub: "0000aaaa-0000-00aa-0a0a-aaaaaa000000", - "custom:cms-roles": "onemac-micro-statesubmitter", - email_verified: true, - given_name: "George", - family_name: "Harrison", - "custom:state": "VA,OH,SC,CO,GA,MD", - email: "george@example.com", - username: "IDM_a853eb41-f5fc-48af-911d-4f478e4da3a2 ", - }, -}; - -describe("helpers", () => { - describe("convertFormAttachments", () => { - it("converts a record of file attachments to an array of key/value pairings", () => { - const testFile = new File([""], "test.pdf"); - const preTransformed: Record = { - test: [testFile], - testAgain: [testFile, testFile], - }; - const transformed = unit.buildAttachmentKeyValueArr(preTransformed); - expect(transformed).toStrictEqual([ - { attachmentKey: "test", file: testFile }, - { attachmentKey: "testAgain", file: testFile }, - { attachmentKey: "testAgain", file: testFile }, - ]); - }); - }); - - describe("urlsToRecipes", () => { - it("takes pre-signed URLs and attachment key/value pairs and returns the upload recipes array", () => { - const testFile = new File([""], "test.pdf"); - const res = unit.urlsToRecipes( - [{ url: "/test", key: "test", bucket: "test-bucket" }], - [{ attachmentKey: "other", file: testFile }], - ); - expect(res).toStrictEqual([ - { - url: "/test", - key: "test", - bucket: "test-bucket", - data: testFile, - title: "other", - name: "test.pdf", - }, - ]); - }); - }); - - describe("buildAttachmentObject", () => { - it("takes UploadRecipe[] and returns Attachment[]", () => { - const attachments = unit.buildAttachmentObject(mockUploadRecipes(3)); - expect(attachments).not.toBeNull(); - expect(attachments).toHaveLength(3); - expect(attachments![0].key).toEqual("test-3"); - expect(attachments![0].filename).toEqual("name-3"); - expect(attachments![0].title).toEqual("other"); - expect(attachments![0].bucket).toEqual("bucket-3"); - expect(attachments![0].uploadDate).not.toBeUndefined(); - }); - }); - - describe("buildSubmissionPayload", () => { - it("builds Action payloads", () => { - const payload: ReturnType = - unit.buildSubmissionPayload( - { test: "data" }, - mockGeorge, - "/default" as SubmissionServiceEndpoint, - Authority.MED_SPA, - mockUploadRecipes(3), - ); - expect(payload.authority).toEqual("medicaid spa"); - expect(payload.origin).toEqual("mako"); - expect(payload.attachments).toHaveLength(3); - expect(payload.test).toEqual("data"); - }); - }); - - it("builds Submission payloads", () => { - const payload: ReturnType = - unit.buildSubmissionPayload( - mockFormData, - mockGeorge, - "/submit", - Authority.MED_SPA, - mockUploadRecipes(3), - ); - expect(payload.authority).toEqual("medicaid spa"); - expect(payload.origin).toEqual("mako"); - expect(payload.attachments).toHaveLength(3); - expect(payload.test).toEqual("data"); - expect(payload.proposedEffectiveDate).toBeTypeOf("number"); - expect(payload.state).toEqual("MD"); - }); -}); diff --git a/react-app/src/api/submissionService.ts b/react-app/src/api/submissionService.ts deleted file mode 100644 index 989a95ad05..0000000000 --- a/react-app/src/api/submissionService.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { API } from "aws-amplify"; -import { - Attachment, - ReactQueryApiError, - Action, - AttachmentKey, - Authority, -} from "shared-types"; -import { buildActionUrl, SubmissionServiceEndpoint } from "@/utils"; -import { OneMacUser } from "@/api"; -import { useMutation, UseMutationOptions } from "@tanstack/react-query"; -import { seaToolFriendlyTimestamp } from "shared-utils"; - -export type SubmissionServiceParameters = { - data: T; - endpoint: SubmissionServiceEndpoint; - user: OneMacUser | undefined; - authority: Authority; -}; -type SubmissionServiceResponse = { - body: { - message: string; - }; -}; -type PreSignedURL = { - url: string; - key: string; - bucket: string; -}; -export type UploadRecipe = PreSignedURL & { - data: File; - title: AttachmentKey; - name: string; -}; -type AttachmentKeyValue = { attachmentKey: AttachmentKey; file: File }; - -/** Pass in an array of UploadRecipes and get a back-end compatible object - * to store attachment data */ -export const buildAttachmentObject = (recipes?: UploadRecipe[]) => { - if (!recipes) return null; - return recipes - .map( - (r) => - ({ - key: r.key, - filename: r.name, - title: r.title, - bucket: r.bucket, - uploadDate: Date.now(), - } as Attachment), - ) - .flat(); -}; - -/** Builds the payload for submission based on which variant a developer has - * configured the {@link submit} function with */ -export const buildSubmissionPayload = >( - data: T, - user: OneMacUser | undefined, - endpoint: SubmissionServiceEndpoint, - authority: Authority, - attachments?: UploadRecipe[], -) => { - const userDetails = { - submitterEmail: user?.user?.email ?? "N/A", - submitterName: - user?.user?.given_name && user?.user?.family_name - ? `${user.user.given_name} ${user.user.family_name}` - : "N/A", - }; - const baseProperties = { - authority: authority, - origin: "mako", - }; - - switch (endpoint) { - case "/appk": - return { - ...data, - ...userDetails, - ...baseProperties, - authority: Authority["1915c"], - proposedEffectiveDate: seaToolFriendlyTimestamp( - data.proposedEffectiveDate as Date, - ), - attachments: attachments ? buildAttachmentObject(attachments) : null, - }; - case buildActionUrl(Action.REMOVE_APPK_CHILD): - return { - ...data, - ...baseProperties, - ...userDetails, - authority: Authority["1915c"], - }; - case "/submit": - return { - ...data, - ...baseProperties, - ...userDetails, - proposedEffectiveDate: seaToolFriendlyTimestamp( - data.proposedEffectiveDate as Date, - ), - attachments: attachments ? buildAttachmentObject(attachments) : null, - state: (data.id as string).split("-")[0], - }; - case buildActionUrl(Action.RESPOND_TO_RAI): - case buildActionUrl(Action.ENABLE_RAI_WITHDRAW): - case buildActionUrl(Action.DISABLE_RAI_WITHDRAW): - case buildActionUrl(Action.WITHDRAW_RAI): - case buildActionUrl(Action.WITHDRAW_PACKAGE): - case buildActionUrl(Action.TEMP_EXTENSION): - case buildActionUrl(Action.UPDATE_ID): - default: - return { - ...baseProperties, - ...userDetails, - ...data, - attachments: attachments ? buildAttachmentObject(attachments) : null, - }; - } -}; - -export const buildAttachmentKeyValueArr = ( - attachments: Record, -): AttachmentKeyValue[] => - Object.entries(attachments) - .filter(([, val]) => val !== undefined && (val as File[]).length) - .map(([key, value]) => { - return (value as File[]).map((file) => ({ - attachmentKey: key as AttachmentKey, - file: file, - })); - }) - .flat(); - -export const urlsToRecipes = ( - urls: PreSignedURL[], - attachments: AttachmentKeyValue[], -): UploadRecipe[] => - urls.map((obj, idx) => ({ - ...obj, // Spreading the presigned url - data: attachments[idx].file, // The attachment file object - // Add your attachments object key and file label value to the attachmentTitleMap - // for this transform to work. Else the title will just be the object key. - title: attachments[idx].attachmentKey, - name: attachments[idx].file.name, - })); - -/** A useful interface for submitting form data to our submission service */ -export const submit = async >({ - data, - endpoint, - user, - authority, -}: SubmissionServiceParameters): Promise => { - if (data?.attachments) { - // Drop nulls and non arrays - const attachments = buildAttachmentKeyValueArr( - data.attachments as Record, - ); - // Generate a presigned url for each attachment - const preSignedURLs: PreSignedURL[] = await Promise.all( - attachments.map((attachment) => - API.post("os", "/getUploadUrl", { - body: { - fileName: attachment.file.name, - }, - }), - ), - ); - // For each attachment, add name, title, and a presigned url... and push to uploadRecipes - const uploadRecipes: UploadRecipe[] = urlsToRecipes( - preSignedURLs, - attachments, - ); - // Upload attachments - await Promise.all( - uploadRecipes.map(async ({ url, data }) => { - await fetch(url, { - body: data, - method: "PUT", - }); - }), - ); - // Submit form data - return await API.post("os", endpoint, { - body: buildSubmissionPayload( - data, - user, - endpoint, - authority, - uploadRecipes, - ), - }); - } else { - // Submit form data - return await API.post("os", endpoint, { - body: buildSubmissionPayload(data, user, endpoint, authority), - }); - } -}; - -/** A useful interface for using react-query with our submission service. If you - * are using react-hook-form's `form.handleSubmit()` pattern, bypass this and just - * use {@link submit}. */ -export const useSubmissionService = >( - config: SubmissionServiceParameters, - options?: UseMutationOptions, -) => - useMutation( - ["submit"], - () => submit(config), - options, - ); diff --git a/react-app/src/api/useGetItem.test.ts b/react-app/src/api/useGetItem.test.ts index 3d1f27150a..19c897db5d 100644 --- a/react-app/src/api/useGetItem.test.ts +++ b/react-app/src/api/useGetItem.test.ts @@ -1,78 +1,47 @@ import { - beforeAll, - afterEach, - afterAll, - it, - expect, - vi, - describe, -} from "vitest"; -import { mockItem } from "./mocks"; + EXISTING_ITEM_APPROVED_AMEND_ID, + EXISTING_ITEM_APPROVED_NEW_ID, + EXISTING_ITEM_APPROVED_RENEW_ID, + EXISTING_ITEM_PENDING_ID, + NOT_FOUND_ITEM_ID, + TEST_ITEM_ID, +} from "mocks"; +import { describe, expect, it } from "vitest"; import * as unit from "./useGetItem"; -import { API } from "aws-amplify"; - -/* When mocking the getItem and helper functions: - * 1. Assign API.post to use mockFetch - * 2. Use ID keywords to change behavior: - * - including "existing" will return an item - * - including "approved" will return 'seatoolStatus: "approved"'*/ - -const mockFetch = vi.fn(async (apiName, path, init) => { - const res = await fetch("/item-mock-server", { - body: JSON.stringify(init.body), - method: "POST", - }); - if (res.status !== 200) - throw Error("useGetItem > mockFetch: Expected error thrown by test."); - return await res.json(); -}); describe("getItem", () => { it("makes an AWS Amplify post request", async () => { - API.post = vi.fn(); - await unit.getItem("test-id"); - expect(API.post).toHaveBeenCalledWith("os", "/item", { - body: { id: "test-id" }, - }); + const item = await unit.getItem(TEST_ITEM_ID); + expect(item).toBeTruthy(); }); }); describe("zod schema helpers", () => { - beforeAll(() => { - mockItem.server.listen(); - API.post = vi.fn(mockFetch); - }); - afterEach(() => mockItem.server.resetHandlers()); - afterAll(() => mockItem.server.close()); describe("idIsApproved", () => { it("returns false if no getItem fails", async () => { - expect(await unit.idIsApproved("not-found")).toBe(false); + expect(await unit.idIsApproved(NOT_FOUND_ITEM_ID)).toBe(false); }); it("returns false if status is not approved", async () => { - expect(await unit.idIsApproved("existing-pending")).toBe(false); + expect(await unit.idIsApproved(EXISTING_ITEM_PENDING_ID)).toBe(false); }); it("returns true if status is approved", async () => { - expect(await unit.idIsApproved("existing-approved")).toBe(true); + expect(await unit.idIsApproved(EXISTING_ITEM_APPROVED_NEW_ID)).toBe(true); }); }); describe("canBeRenewedOrAmended", () => { it("returns true if item is New or Renew actionType", async () => { - const newCanRenewOrAmend = await unit.canBeRenewedOrAmended( - "existing-approved-actionType=New", - ); + const newCanRenewOrAmend = await unit.canBeRenewedOrAmended(EXISTING_ITEM_APPROVED_NEW_ID); const renewCanRenewOrAmend = await unit.canBeRenewedOrAmended( - "existing-approved-actionType=Renew", + EXISTING_ITEM_APPROVED_RENEW_ID, ); expect(newCanRenewOrAmend).toBe(true); expect(renewCanRenewOrAmend).toBe(true); }); it("returns false if an item is Amend actionType", async () => { - expect( - await unit.canBeRenewedOrAmended("existing-approved-actionType=Amend"), - ).toBe(false); + expect(await unit.canBeRenewedOrAmended(EXISTING_ITEM_APPROVED_AMEND_ID)).toBe(false); }); }); }); diff --git a/react-app/src/api/useGetTypes.test.ts b/react-app/src/api/useGetTypes.test.ts index df392ce560..7e6a835ba4 100644 --- a/react-app/src/api/useGetTypes.test.ts +++ b/react-app/src/api/useGetTypes.test.ts @@ -1,39 +1,7 @@ -import { - beforeAll, - afterEach, - afterAll, - it, - expect, - vi, - describe, -} from "vitest"; -import { API } from "aws-amplify"; -import { mockTypes } from "./mocks"; +import { describe, expect, it } from "vitest"; import { fetchData } from "./useGetTypes"; -const mockFetchData = vi.fn(async (apiName, path, init) => { - const endpoint = init.body.typeIds ? "/getSubTypes" : "/getTypes"; - const res = await fetch(`/os${endpoint}`, { - body: JSON.stringify(init.body), - method: "POST", - }); - if (res.status !== 200) - throw Error("useGetData > mockFetch: Expected error thrown by test."); - return await res.json(); -}); - describe("fetchData", () => { - beforeAll(() => { - mockTypes.server.listen(); - API.post = vi.fn(mockFetchData); - }); - afterEach(() => { - mockTypes.server.resetHandlers(); - }); - afterAll(() => { - mockTypes.server.close(); - }); - describe("fetchTypes", () => { it("makes an AWS Amplify post request for types", async () => { const types = await fetchData({ authorityId: 1 }); @@ -41,17 +9,11 @@ describe("fetchData", () => { { id: 101, authorityId: 1, name: "typeOne" }, { id: 102, authorityId: 1, name: "typetwo" }, ]); - expect(API.post).toHaveBeenCalledWith("os", "/getTypes", { - body: { authorityId: 1 }, - }); }); it("successfully fetches types for a given authorityId", async () => { const types = await fetchData({ authorityId: 2 }); expect(types).toEqual([{ id: 103, authorityId: 2, name: "typethree" }]); - expect(API.post).toHaveBeenCalledWith("os", "/getTypes", { - body: { authorityId: 2 }, - }); }); it("returns an empty array when there are no types", async () => { @@ -60,9 +22,7 @@ describe("fetchData", () => { }); it("throws an error when fetch fails", async () => { - await expect(fetchData({ authorityId: -1 })).rejects.toThrow( - "Failed to fetch types", - ); + await expect(fetchData({ authorityId: -1 })).rejects.toThrow("Failed to fetch types"); }); }); @@ -73,19 +33,11 @@ describe("fetchData", () => { { id: 101, authorityId: 1, name: "subtypeOne", typeId: 1 }, { id: 102, authorityId: 1, name: "subtypetwo", typeId: 2 }, ]); - expect(API.post).toHaveBeenCalledWith("os", "/getSubTypes", { - body: { authorityId: 1, typeIds: [1, 2] }, - }); }); it("successfully fetches subtypes for a given authorityId and typeIds", async () => { const subtypes = await fetchData({ authorityId: 2, typeIds: [4] }); - expect(subtypes).toEqual([ - { id: 104, authorityId: 2, name: "subtypethree", typeId: 4 }, - ]); - expect(API.post).toHaveBeenCalledWith("os", "/getSubTypes", { - body: { authorityId: 2, typeIds: [4] }, - }); + expect(subtypes).toEqual([{ id: 104, authorityId: 2, name: "subtypethree", typeId: 4 }]); }); it("returns an empty array when there are no subtypes", async () => { diff --git a/react-app/src/api/useGetUser.ts b/react-app/src/api/useGetUser.ts index 3c2ce395cc..bf9e1eb7f1 100644 --- a/react-app/src/api/useGetUser.ts +++ b/react-app/src/api/useGetUser.ts @@ -12,9 +12,11 @@ export type OneMacUser = { export const getUser = async (): Promise => { try { const currentAuthenticatedUser = await Auth.currentAuthenticatedUser(); - const userAttributesArray = await Auth.userAttributes( - currentAuthenticatedUser, - ); + if (!currentAuthenticatedUser) { + return { user: null } satisfies OneMacUser; + } + const userAttributesArray = (await Auth.userAttributes(currentAuthenticatedUser)) || []; + // Set object up with key/values from attributes array const userAttributesObj = userAttributesArray.reduce( (obj, item) => @@ -27,9 +29,9 @@ export const getUser = async (): Promise => { {} as CognitoUserAttributes, ); // Manual additions and normalizations - userAttributesObj["custom:cms-roles"] = - userAttributesObj?.["custom:cms-roles"] || ""; - userAttributesObj.username = currentAuthenticatedUser?.username || ""; + userAttributesObj["custom:cms-roles"] = userAttributesObj["custom:cms-roles"] || ""; + + userAttributesObj.username = currentAuthenticatedUser.username || ""; return { user: userAttributesObj, diff --git a/react-app/src/assets/abp/ABP1.pdf b/react-app/src/assets/abp/ABP1.pdf new file mode 100644 index 0000000000..fdac1a9589 Binary files /dev/null and b/react-app/src/assets/abp/ABP1.pdf differ diff --git a/react-app/src/assets/abp/ABP10.pdf b/react-app/src/assets/abp/ABP10.pdf new file mode 100644 index 0000000000..921cfe28d9 Binary files /dev/null and b/react-app/src/assets/abp/ABP10.pdf differ diff --git a/react-app/src/assets/abp/ABP11.pdf b/react-app/src/assets/abp/ABP11.pdf new file mode 100644 index 0000000000..4cfaac1e1a Binary files /dev/null and b/react-app/src/assets/abp/ABP11.pdf differ diff --git a/react-app/src/assets/abp/ABP2a.pdf b/react-app/src/assets/abp/ABP2a.pdf new file mode 100644 index 0000000000..50d20f68d7 Binary files /dev/null and b/react-app/src/assets/abp/ABP2a.pdf differ diff --git a/react-app/src/assets/abp/ABP2b.pdf b/react-app/src/assets/abp/ABP2b.pdf new file mode 100644 index 0000000000..64b146cb12 Binary files /dev/null and b/react-app/src/assets/abp/ABP2b.pdf differ diff --git a/react-app/src/assets/abp/ABP3.1.pdf b/react-app/src/assets/abp/ABP3.1.pdf new file mode 100644 index 0000000000..1e3caf220e Binary files /dev/null and b/react-app/src/assets/abp/ABP3.1.pdf differ diff --git a/react-app/src/assets/abp/ABP3.pdf b/react-app/src/assets/abp/ABP3.pdf new file mode 100644 index 0000000000..11d516c071 Binary files /dev/null and b/react-app/src/assets/abp/ABP3.pdf differ diff --git a/react-app/src/assets/abp/ABP4.pdf b/react-app/src/assets/abp/ABP4.pdf new file mode 100644 index 0000000000..226b4d2bc4 Binary files /dev/null and b/react-app/src/assets/abp/ABP4.pdf differ diff --git a/react-app/src/assets/abp/ABP5.pdf b/react-app/src/assets/abp/ABP5.pdf new file mode 100644 index 0000000000..024d7a267f Binary files /dev/null and b/react-app/src/assets/abp/ABP5.pdf differ diff --git a/react-app/src/assets/abp/ABP6.pdf b/react-app/src/assets/abp/ABP6.pdf new file mode 100644 index 0000000000..a803f1e6b5 Binary files /dev/null and b/react-app/src/assets/abp/ABP6.pdf differ diff --git a/react-app/src/assets/abp/ABP7.pdf b/react-app/src/assets/abp/ABP7.pdf new file mode 100644 index 0000000000..7bbc07dc1f Binary files /dev/null and b/react-app/src/assets/abp/ABP7.pdf differ diff --git a/react-app/src/assets/abp/ABP8.pdf b/react-app/src/assets/abp/ABP8.pdf new file mode 100644 index 0000000000..be74352155 Binary files /dev/null and b/react-app/src/assets/abp/ABP8.pdf differ diff --git a/react-app/src/assets/abp/ABP9.pdf b/react-app/src/assets/abp/ABP9.pdf new file mode 100644 index 0000000000..b78859ac95 Binary files /dev/null and b/react-app/src/assets/abp/ABP9.pdf differ diff --git a/react-app/src/assets/chp/CS10.pdf b/react-app/src/assets/chp/CS10.pdf new file mode 100644 index 0000000000..d20d4e503f Binary files /dev/null and b/react-app/src/assets/chp/CS10.pdf differ diff --git a/react-app/src/assets/chp/CS11.pdf b/react-app/src/assets/chp/CS11.pdf new file mode 100644 index 0000000000..b884b2b68a Binary files /dev/null and b/react-app/src/assets/chp/CS11.pdf differ diff --git a/react-app/src/assets/chp/CS12.pdf b/react-app/src/assets/chp/CS12.pdf new file mode 100644 index 0000000000..45e471e8b8 Binary files /dev/null and b/react-app/src/assets/chp/CS12.pdf differ diff --git a/react-app/src/assets/chp/CS13.pdf b/react-app/src/assets/chp/CS13.pdf new file mode 100644 index 0000000000..713309696d Binary files /dev/null and b/react-app/src/assets/chp/CS13.pdf differ diff --git a/react-app/src/assets/chp/CS14.pdf b/react-app/src/assets/chp/CS14.pdf new file mode 100644 index 0000000000..216a3fb28e Binary files /dev/null and b/react-app/src/assets/chp/CS14.pdf differ diff --git a/react-app/src/assets/chp/CS15.pdf b/react-app/src/assets/chp/CS15.pdf new file mode 100644 index 0000000000..7ac9495cf9 Binary files /dev/null and b/react-app/src/assets/chp/CS15.pdf differ diff --git a/react-app/src/assets/chp/CS15a.pdf b/react-app/src/assets/chp/CS15a.pdf new file mode 100644 index 0000000000..e2e4221678 Binary files /dev/null and b/react-app/src/assets/chp/CS15a.pdf differ diff --git a/react-app/src/assets/chp/CS16.pdf b/react-app/src/assets/chp/CS16.pdf new file mode 100644 index 0000000000..2574d1c42d Binary files /dev/null and b/react-app/src/assets/chp/CS16.pdf differ diff --git a/react-app/src/assets/chp/CS17.pdf b/react-app/src/assets/chp/CS17.pdf new file mode 100644 index 0000000000..44e9e39f84 Binary files /dev/null and b/react-app/src/assets/chp/CS17.pdf differ diff --git a/react-app/src/assets/chp/CS18.pdf b/react-app/src/assets/chp/CS18.pdf new file mode 100644 index 0000000000..08b67dfb81 Binary files /dev/null and b/react-app/src/assets/chp/CS18.pdf differ diff --git a/react-app/src/assets/chp/CS19.pdf b/react-app/src/assets/chp/CS19.pdf new file mode 100644 index 0000000000..f45327d7f1 Binary files /dev/null and b/react-app/src/assets/chp/CS19.pdf differ diff --git a/react-app/src/assets/chp/CS20.pdf b/react-app/src/assets/chp/CS20.pdf new file mode 100644 index 0000000000..ceaa343e2c Binary files /dev/null and b/react-app/src/assets/chp/CS20.pdf differ diff --git a/react-app/src/assets/chp/CS21.pdf b/react-app/src/assets/chp/CS21.pdf new file mode 100644 index 0000000000..aa57d586c2 Binary files /dev/null and b/react-app/src/assets/chp/CS21.pdf differ diff --git a/react-app/src/assets/chp/CS23.pdf b/react-app/src/assets/chp/CS23.pdf new file mode 100644 index 0000000000..e6f4376f4d Binary files /dev/null and b/react-app/src/assets/chp/CS23.pdf differ diff --git a/react-app/src/assets/chp/CS24.pdf b/react-app/src/assets/chp/CS24.pdf new file mode 100644 index 0000000000..1f51a66333 Binary files /dev/null and b/react-app/src/assets/chp/CS24.pdf differ diff --git a/react-app/src/assets/chp/CS27.pdf b/react-app/src/assets/chp/CS27.pdf new file mode 100644 index 0000000000..9d03b63277 Binary files /dev/null and b/react-app/src/assets/chp/CS27.pdf differ diff --git a/react-app/src/assets/chp/CS28.pdf b/react-app/src/assets/chp/CS28.pdf new file mode 100644 index 0000000000..6114aab413 Binary files /dev/null and b/react-app/src/assets/chp/CS28.pdf differ diff --git a/react-app/src/assets/chp/CS29.pdf b/react-app/src/assets/chp/CS29.pdf new file mode 100644 index 0000000000..0af5fb1172 Binary files /dev/null and b/react-app/src/assets/chp/CS29.pdf differ diff --git a/react-app/src/assets/chp/CS3.pdf b/react-app/src/assets/chp/CS3.pdf new file mode 100644 index 0000000000..034882eafc Binary files /dev/null and b/react-app/src/assets/chp/CS3.pdf differ diff --git a/react-app/src/assets/chp/CS30.pdf b/react-app/src/assets/chp/CS30.pdf new file mode 100644 index 0000000000..b694e680f2 Binary files /dev/null and b/react-app/src/assets/chp/CS30.pdf differ diff --git a/react-app/src/assets/chp/CS7.pdf b/react-app/src/assets/chp/CS7.pdf new file mode 100644 index 0000000000..9a32880bdc Binary files /dev/null and b/react-app/src/assets/chp/CS7.pdf differ diff --git a/react-app/src/assets/chp/CS8.pdf b/react-app/src/assets/chp/CS8.pdf new file mode 100644 index 0000000000..57fd68ab7b Binary files /dev/null and b/react-app/src/assets/chp/CS8.pdf differ diff --git a/react-app/src/assets/chp/CS9.pdf b/react-app/src/assets/chp/CS9.pdf new file mode 100644 index 0000000000..7f7fb35353 Binary files /dev/null and b/react-app/src/assets/chp/CS9.pdf differ diff --git a/react-app/src/assets/mpc/G1.pdf b/react-app/src/assets/mpc/G1.pdf new file mode 100644 index 0000000000..8428247cba Binary files /dev/null and b/react-app/src/assets/mpc/G1.pdf differ diff --git a/react-app/src/assets/mpc/G2a.pdf b/react-app/src/assets/mpc/G2a.pdf new file mode 100644 index 0000000000..9460a24ff8 Binary files /dev/null and b/react-app/src/assets/mpc/G2a.pdf differ diff --git a/react-app/src/assets/mpc/G2b.pdf b/react-app/src/assets/mpc/G2b.pdf new file mode 100644 index 0000000000..2493929b73 Binary files /dev/null and b/react-app/src/assets/mpc/G2b.pdf differ diff --git a/react-app/src/assets/mpc/G2c.pdf b/react-app/src/assets/mpc/G2c.pdf new file mode 100644 index 0000000000..b0937fd53c Binary files /dev/null and b/react-app/src/assets/mpc/G2c.pdf differ diff --git a/react-app/src/assets/mpc/G3.pdf b/react-app/src/assets/mpc/G3.pdf new file mode 100644 index 0000000000..467f073323 Binary files /dev/null and b/react-app/src/assets/mpc/G3.pdf differ diff --git a/react-app/src/assets/mpc/IG_G2c_CostSharingAmountsTargeting.pdf b/react-app/src/assets/mpc/IG_G2c_CostSharingAmountsTargeting.pdf index a5affa14da..dc731407ef 100644 Binary files a/react-app/src/assets/mpc/IG_G2c_CostSharingAmountsTargeting.pdf and b/react-app/src/assets/mpc/IG_G2c_CostSharingAmountsTargeting.pdf differ diff --git a/react-app/src/assets/mpc/IG_G3_CostSharingLimitations.pdf b/react-app/src/assets/mpc/IG_G3_CostSharingLimitations.pdf new file mode 100644 index 0000000000..b4f4fc507a Binary files /dev/null and b/react-app/src/assets/mpc/IG_G3_CostSharingLimitations.pdf differ diff --git a/react-app/src/components/ActionForm/ActionForm.test.tsx b/react-app/src/components/ActionForm/ActionForm.test.tsx index 26eb0ee72f..dc94c89fa1 100644 --- a/react-app/src/components/ActionForm/ActionForm.test.tsx +++ b/react-app/src/components/ActionForm/ActionForm.test.tsx @@ -1,39 +1,31 @@ -import { fireEvent, waitFor } from "@testing-library/react"; +import { fireEvent, waitFor, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, test, expect, vi } from "vitest"; import { ActionForm } from "./index"; import { z } from "zod"; import { attachmentArraySchemaOptional, SEATOOL_STATUS } from "shared-types"; +import { + SUBMISSION_ERROR_ITEM_ID, + GET_ERROR_ITEM_ID, + setDefaultStateSubmitter, + setDefaultReviewer, +} from "mocks"; import * as userPrompt from "@/components/ConfirmationDialog/userPrompt"; import * as banner from "@/components/Banner/banner"; import * as documentPoller from "@/utils/Poller/documentPoller"; -import { renderForm } from "@/utils/test-helpers/renderForm"; +import { DataPoller } from "@/utils/Poller/DataPoller"; +import { EXISTING_ITEM_PENDING_ID } from "mocks"; +import { + renderFormAsync, + renderFormWithPackageSectionAsync, +} from "@/utils/test-helpers/renderForm"; import { isCmsReadonlyUser } from "shared-utils"; -import { API as awsAmplifyAPI } from "aws-amplify"; - -vi.mock("aws-amplify", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...(actual as object), - API: { - post: vi.fn(), - }, - }; -}); - -vi.mock("../../utils/Poller/DataPoller", () => { - return { - DataPoller: vi.fn().mockImplementation(() => ({ - startPollingData: vi.fn(() => Promise.resolve()), - })), - }; -}); const PROGRESS_REMINDER = /If you leave this page, you will lose your progress on this form./; describe("ActionForm", () => { - test("renders `breadcrumbText`", () => { - const { queryByText } = renderForm( + test("renders `breadcrumbText`", async () => { + await renderFormAsync( { />, ); - expect(queryByText("Example Breadcrumb")).toBeInTheDocument(); + expect(screen.queryByText("Example Breadcrumb")).toBeInTheDocument(); }); - test("renders `title`", () => { - const { queryByText } = renderForm( + test("renders `title`", async () => { + await renderFormAsync( { />, ); - expect(queryByText("Action Form Title")).toBeInTheDocument(); + expect(screen.queryByText("Action Form Title")).toBeInTheDocument(); }); - test("renders `attachments.faqLink`", () => { - const { queryByText } = renderForm( + test("renders `attachments.faqLink`", async () => { + await renderFormAsync( { />, ); - expect(queryByText("FAQ Page")).toHaveAttribute("href", "/hello-world-link"); + expect(screen.queryByText("FAQ Page")).toHaveAttribute("href", "/hello-world-link"); }); - test("renders `attachments.title`", () => { - const { queryByText } = renderForm( + test("renders `attachments.title`", async () => { + await renderFormAsync( { />, ); - expect(queryByText("this is an attachments title")).toBeInTheDocument(); + expect(screen.queryByText("this is an attachments title")).toBeInTheDocument(); }); - test("doesn't render form if user access is denied", () => { - const { queryByText } = renderForm( + test("doesn't render form if user access is denied", async () => { + setDefaultReviewer(); + + await renderFormAsync( { />, ); - expect(queryByText("Action Form Title")).not.toBeInTheDocument(); + expect(screen.queryByText("Action Form Title")).not.toBeInTheDocument(); + setDefaultStateSubmitter(); }); - test("renders `defaultValues` in appropriate input", () => { - const { queryByDisplayValue } = renderForm( + test("renders `defaultValues` in appropriate input", async () => { + await renderFormAsync( { />, ); - expect(queryByDisplayValue("default value for id")).toBeInTheDocument(); + expect(screen.queryByDisplayValue("default value for id")).toBeInTheDocument(); }); - test("renders `attachments.instructions`", () => { - const { queryByText } = renderForm( + test("renders `attachments.instructions`", async () => { + await renderFormAsync( { />, ); - expect(queryByText(/hello world special instructions./)).toBeInTheDocument(); + expect(screen.queryByText(/hello world special instructions./)).toBeInTheDocument(); }); - test("renders `attachments.callout`", () => { - const { queryByText } = renderForm( + test("renders `attachments.callout`", async () => { + await renderFormAsync( { />, ); - expect(queryByText(/this is a callout/)).toBeInTheDocument(); + expect(screen.queryByText(/this is a callout/)).toBeInTheDocument(); }); test("renders custom `promptOnLeavingForm` when clicking Cancel", async () => { - const { container } = renderForm( + await renderFormAsync( { .spyOn(userPrompt, "userPrompt") .mockImplementation((args) => (args.onAccept = onAcceptMock)); - const cancelBtn = container.querySelector('button[type="reset"]'); + const cancelBtn = await screen.findByTestId("cancel-action-form"); await userEvent.click(cancelBtn); expect(userPromptSpy).toBeCalledWith({ @@ -248,9 +243,9 @@ describe("ActionForm", () => { onAccept: onAcceptMock, }); }); - + // test("renders custom `promptPreSubmission` when clicking Submit", async () => { - const { container } = renderForm( + await renderFormAsync( { .spyOn(userPrompt, "userPrompt") .mockImplementation((args) => (args.onAccept = onAcceptMock)); - const submitBtn = container.querySelector('button[type="button"]'); + const submitBtn = await screen.findByTestId("submit-action-form"); await userEvent.click(submitBtn); expect(userPromptSpy).toBeCalledWith({ @@ -286,71 +281,48 @@ describe("ActionForm", () => { }); test("calls `documentPoller` with `documentPollerArgs`", async () => { - const mockStatusChecks = { - recordExists: true, - hasStatus: vi.fn(() => true), - }; - - const documentPollerSpy = vi - .spyOn(documentPoller, "documentPoller") - // @ts-ignore - mocking documentPollerSpy expects private class members - .mockImplementation(() => ({ - startPollingData: vi.fn().mockResolvedValue({ - maxAttemptsReached: false, - correctDataStateFound: true, - }), - options: { - fetcher: vi.fn().mockResolvedValue({}), - onPoll: vi.fn().mockImplementationOnce(() => mockStatusChecks), - pollAttempts: 1, - interval: 1000, - }, - })); + const documentPollerSpy = vi.spyOn(documentPoller, "documentPoller"); + const documentChecker: documentPoller.CheckDocumentFunction = (checker) => + checker.hasStatus(SEATOOL_STATUS.PENDING); - const { container } = renderForm( + await renderFormAsync( null} documentPollerArgs={{ - property: () => "id", - documentChecker: (checker) => checker.hasStatus(SEATOOL_STATUS.PENDING), + property: "id", + documentChecker, }} breadcrumbText="Example Breadcrumb" />, ); - const form = container.querySelector("form"); - fireEvent.submit(form); + fireEvent.submit(await screen.findByTestId("submit-action-form")); - await waitFor(() => { - expect(documentPollerSpy).toHaveBeenCalledWith("id", expect.any(Function)); - }); - - const [, checkerFn] = documentPollerSpy.mock.lastCall; - - // @ts-expect-error - mocking status checks expects all declared status checks - const resultValue = checkerFn(mockStatusChecks); - - expect(mockStatusChecks.hasStatus).toHaveBeenCalledWith(SEATOOL_STATUS.PENDING); - - expect(resultValue).toBe(true); + await waitFor(() => + expect(documentPollerSpy).toHaveBeenCalledWith(EXISTING_ITEM_PENDING_ID, documentChecker), + ); }); test("calls `banner` with `bannerPostSubmission`", async () => { - const { container } = renderForm( + const dataPollerSpy = vi.spyOn(DataPoller.prototype, "startPollingData"); + const bannerSpy = vi.spyOn(banner, "banner"); + + await renderFormWithPackageSectionAsync( null} documentPollerArgs={{ - property: () => "id", + property: "id", documentChecker: () => true, }} bannerPostSubmission={{ @@ -359,60 +331,188 @@ describe("ActionForm", () => { }} breadcrumbText="Example Breadcrumb" />, + EXISTING_ITEM_PENDING_ID, ); - const bannerPollerSpy = vi.spyOn(banner, "banner"); + const submitButton = await screen.findByTestId("submit-action-form"); - const form = container.querySelector("form"); - fireEvent.submit(form); + vi.useFakeTimers(); - await waitFor(() => - expect(bannerPollerSpy).toBeCalledWith({ + fireEvent.submit(submitButton); + + await vi.waitFor(async () => { + await vi.runAllTimersAsync(); + expect(dataPollerSpy).toHaveResolvedWith({ + correctDataStateFound: true, + maxAttemptsReached: false, + }); + }); + + await vi.waitFor(() => + expect(bannerSpy).toBeCalledWith({ header: "Hello World Header", body: "Hello World Body", pathnameToDisplayOn: "/dashboard", }), ); + + vi.useRealTimers(); }); test("calls error banner if submission fails", async () => { - vi.spyOn(awsAmplifyAPI, "post").mockImplementationOnce( - vi.fn().mockRejectedValue("Intentional failure"), - ); + const bannerSpy = vi.spyOn(banner, "banner"); - const { container } = renderForm( + await renderFormWithPackageSectionAsync( null} documentPollerArgs={{ - property: () => "id", + property: "id", documentChecker: () => true, }} + bannerPostSubmission={{ + header: "Hello World Header", + body: "Hello World Body", + }} breadcrumbText="Example Breadcrumb" />, + SUBMISSION_ERROR_ITEM_ID, ); - const bannerPollerSpy = vi.spyOn(banner, "banner"); + const submitButton = await screen.findByTestId("submit-action-form"); - const form = container.querySelector("form"); - fireEvent.submit(form); + fireEvent.submit(submitButton); - await waitFor(() => - expect(bannerPollerSpy).toBeCalledWith({ + await vi.waitFor(() => + expect(bannerSpy).toBeCalledWith({ header: "An unexpected error has occurred:", - body: "Intentional failure", + body: "Error submitting form: Request failed with status code 500", pathnameToDisplayOn: "/", variant: "destructive", }), ); }); + test("calls error banner on submit if fetch fails", async () => { + const dataPollerSpy = vi.spyOn(DataPoller.prototype, "startPollingData"); + const bannerSpy = vi.spyOn(banner, "banner"); + + await renderFormWithPackageSectionAsync( + null} + documentPollerArgs={{ + property: "id", + documentChecker: () => true, + }} + bannerPostSubmission={{ + header: "Hello World Header", + body: "Hello World Body", + }} + breadcrumbText="Example Breadcrumb" + />, + GET_ERROR_ITEM_ID, + ); + + const submitBtn = await screen.findByTestId("submit-action-form"); + + vi.useFakeTimers(); + + fireEvent.submit(submitBtn); + + await vi.waitFor(async () => { + await vi.runAllTimersAsync(); + expect(dataPollerSpy.mock.settledResults).toEqual([ + { + type: "rejected", + value: { + correctDataStateFound: false, + maxAttemptsReached: true, + message: "Error fetching data: Request failed with status code 500", + }, + }, + ]); + }); + + await vi.waitFor(() => + expect(bannerSpy).toBeCalledWith({ + header: "An unexpected error has occurred:", + body: "Error fetching data: Request failed with status code 500", + pathnameToDisplayOn: "/", + variant: "destructive", + }), + ); + + vi.useRealTimers(); + }, 30000); + + test("calls error banner on submit if document check fails", async () => { + const dataPollerSpy = vi.spyOn(DataPoller.prototype, "startPollingData"); + const bannerSpy = vi.spyOn(banner, "banner"); + + await renderFormWithPackageSectionAsync( + null} + documentPollerArgs={{ + property: "id", + documentChecker: () => false, + }} + bannerPostSubmission={{ + header: "Hello World Header", + body: "Hello World Body", + }} + breadcrumbText="Example Breadcrumb" + />, + EXISTING_ITEM_PENDING_ID, + ); + + const submitBtn = await screen.findByTestId("submit-action-form"); + + vi.useFakeTimers(); + + fireEvent.submit(submitBtn); + + await vi.waitFor(async () => { + await vi.runAllTimersAsync(); + expect(dataPollerSpy.mock.settledResults).toEqual([ + { + type: "rejected", + value: { + correctDataStateFound: false, + maxAttemptsReached: true, + message: "Error polling data: Correct data state not found, after max attempts reached", + }, + }, + ]); + }); + + await vi.waitFor(() => + expect(bannerSpy).toBeCalledWith({ + header: "An unexpected error has occurred:", + body: "Error polling data: Correct data state not found, after max attempts reached", + pathnameToDisplayOn: "/", + variant: "destructive", + }), + ); + + vi.useRealTimers(); + }, 30000); + test("renders all attachment properties within `attachments`", async () => { - const { queryAllByText } = renderForm( + await renderFormAsync( { />, ); - const otherAttachmentLabels = queryAllByText("Other"); + const otherAttachmentLabels = screen.queryAllByText("Other"); expect(otherAttachmentLabels.length).toBe(3); }); - test("renders Additional Information if `additionalInformation` is defined in schema", () => { - const { queryByText } = renderForm( + test("renders Additional Information if `additionalInformation` is defined in schema", async () => { + await renderFormAsync( { />, ); - expect(queryByText("Additional Information")).toBeInTheDocument(); + expect(screen.queryByText("Additional Information")).toBeInTheDocument(); }); - test("doesn't render Additional Information if `additionalInformation` is undefined in schema", () => { - const { queryByText } = renderForm( + test("doesn't render Additional Information if `additionalInformation` is undefined in schema", async () => { + await renderFormAsync( { />, ); - expect(queryByText("Additional Information")).not.toBeInTheDocument(); + expect(screen.queryByText("Additional Information")).not.toBeInTheDocument(); }); - test("renders Attachments if `attachments` is defined in schema", () => { - const { queryByText } = renderForm( + test("renders Attachments if `attachments` is defined in schema", async () => { + await renderFormAsync( { />, ); - expect(queryByText("Attachments")).toBeInTheDocument(); - expect(queryByText("Other")).toBeInTheDocument(); + expect(screen.queryByText("Attachments")).toBeInTheDocument(); + expect(screen.queryByText("Other")).toBeInTheDocument(); }); - test("doesn't render Attachments if `attachments` is undefined in schema", () => { - const { queryByText } = renderForm( + test("doesn't render Attachments if `attachments` is undefined in schema", async () => { + await renderFormAsync( { />, ); - expect(queryByText("Attachments")).not.toBeInTheDocument(); + expect(screen.queryByText("Attachments")).not.toBeInTheDocument(); }); - test("renders ProgressReminder if schema has `attachments` property", () => { - const { queryAllByText } = renderForm( + test("renders ProgressReminder if schema has `attachments` property", async () => { + await renderFormAsync( { />, ); - expect(queryAllByText(PROGRESS_REMINDER).length).toBe(2); + expect(screen.queryAllByText(PROGRESS_REMINDER).length).toBe(2); }); - test("renders ProgressReminder if `areFieldsRequired` property is undefined", () => { - const { queryAllByText } = renderForm( + test("renders ProgressReminder if `areFieldsRequired` property is undefined", async () => { + await renderFormAsync( { />, ); - expect(queryAllByText(PROGRESS_REMINDER).length).toBe(2); + expect(screen.queryAllByText(PROGRESS_REMINDER).length).toBe(2); }); - test("doesn't render ProgressReminder if `areFieldsRequired` is false", () => { - const { queryByText } = renderForm( + test("doesn't render ProgressReminder if `areFieldsRequired` is false", async () => { + await renderFormAsync( { />, ); - expect(queryByText(PROGRESS_REMINDER)).not.toBeInTheDocument(); - }); - - test("renders default wrapper if `fieldsLayout` is undefined", () => { - const { queryAllByText } = renderForm( - ( - <> -
-
- - )} - documentPollerArgs={{ - property: () => "id", - documentChecker: () => true, - }} - breadcrumbText="Example Breadcrumb" - />, - ); - - expect( - queryAllByText(/Once you submit this form, a confirmation email is sent to you and to CMS./) - .length, - ).toBe(2); + expect(screen.queryByText(PROGRESS_REMINDER)).not.toBeInTheDocument(); }); }); diff --git a/react-app/src/components/ActionForm/ActionFormAttachments.tsx b/react-app/src/components/ActionForm/ActionFormAttachments.tsx index d5195eff06..d1f4c671c9 100644 --- a/react-app/src/components/ActionForm/ActionFormAttachments.tsx +++ b/react-app/src/components/ActionForm/ActionFormAttachments.tsx @@ -40,12 +40,13 @@ export const ActionFormAttachments = ({ const form = useFormContext(); const attachmentInstructions = instructions ?? [ - , , + , ]; return ( {title} {requiredIndicatorForTitle && } diff --git a/react-app/src/components/ActionForm/actionForm.components.tsx b/react-app/src/components/ActionForm/actionForm.components.tsx index 234a7293e7..3f628ea1fb 100644 --- a/react-app/src/components/ActionForm/actionForm.components.tsx +++ b/react-app/src/components/ActionForm/actionForm.components.tsx @@ -1,9 +1,9 @@ import { FAQ_TAB } from "@/router"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { z } from "zod"; export const AttachmentFileFormatInstructions = () => ( -

+

We accept the following file formats:{" "} .doc, .docx, .pdf, .jpg, .xlsx, and more. {" "} ); const isZodArrayDef = (def: unknown): def is z.ZodArrayDef => - def && typeof def === "object" && "typeName" in def && def.typeName === "ZodArray"; + def !== undefined && + def !== null && + typeof def === "object" && + "typeName" in def && + def.typeName === "ZodArray"; export const AttachmentInstructions = ({ fileValidation }: { fileValidation: z.ZodArray }) => { if (isZodArrayDef(fileValidation)) { diff --git a/react-app/src/components/ActionForm/index.tsx b/react-app/src/components/ActionForm/index.tsx index 97d54fbf98..eedf902bc5 100644 --- a/react-app/src/components/ActionForm/index.tsx +++ b/react-app/src/components/ActionForm/index.tsx @@ -21,7 +21,7 @@ import { import { DefaultValues, FieldPath, useForm, UseFormReturn } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; -import { Navigate, useLocation, useNavigate, useParams } from "react-router-dom"; +import { Navigate, useLocation, useNavigate, useParams } from "react-router"; import { getFormOrigin } from "@/utils"; import { CheckDocumentFunction, documentPoller } from "@/utils/Poller/documentPoller"; import { API } from "aws-amplify"; @@ -84,7 +84,7 @@ type ActionFormProps = { export const ActionForm = ({ schema, - defaultValues, + defaultValues = {} as DefaultValues>>, title, fields: Fields, bannerPostSubmission = { @@ -125,7 +125,7 @@ export const ActionForm = ({ const { pathname } = useLocation(); const navigate = useNavigate(); - const { data: userObj } = useGetUser(); + const { data: userObj, isLoading: isUserLoading } = useGetUser(); const breadcrumbs = optionCrumbsFromPath(pathname, authority, id); @@ -149,23 +149,42 @@ export const ActionForm = ({ const onSubmit = form.handleSubmit(async (formData) => { try { - await mutateAsync(formData); + try { + await mutateAsync(formData); + } catch (error) { + throw Error( + `Error submitting form: ${ + error?.message || error + }`, + ); + } const { documentChecker, property } = documentPollerArgs; const documentPollerId = typeof property === "function" ? property(formData) : formData[property]; - const poller = documentPoller(documentPollerId, documentChecker); - await poller.startPollingData(); + try { + const poller = documentPoller(documentPollerId, documentChecker); + await poller.startPollingData(); + } catch (error) { + const message = `${ + error?.message || error + }`; + throw Error(message); + } const formOrigins = getFormOrigin({ authority, id }); - banner({ - ...bannerPostSubmission, - pathnameToDisplayOn: formOrigins.pathname, - }); navigate(formOrigins); + + // artificially delaying allows the banner to be displayed after navigation + setTimeout(() => { + banner({ + ...bannerPostSubmission, + pathnameToDisplayOn: formOrigins.pathname, + }); + }, 50); } catch (error) { console.error(error); banner({ @@ -180,11 +199,15 @@ export const ActionForm = ({ const attachmentsFromSchema = useMemo(() => getAttachments(schema), [schema]); + if (isUserLoading === true) { + return ; + } + const doesUserHaveAccessToForm = conditionsDeterminingUserAccess.some((condition) => - condition(userObj.user), + condition(userObj?.user || null), ); - if (doesUserHaveAccessToForm === false) { + if (!userObj || doesUserHaveAccessToForm === false) { return ; } @@ -203,7 +226,7 @@ export const ActionForm = ({ {form.formState.isSubmitting && } - +

{areFieldsRequired && } @@ -217,6 +240,7 @@ export const ActionForm = ({ )} {additionalInformation && ( {additionalInformation.title}{" "} diff --git a/react-app/src/components/Banner/banner.test.tsx b/react-app/src/components/Banner/banner.test.tsx index 95a95458bb..b326b4c3e2 100644 --- a/react-app/src/components/Banner/banner.test.tsx +++ b/react-app/src/components/Banner/banner.test.tsx @@ -1,5 +1,5 @@ import { ReactNode } from "react"; -import { Link, MemoryRouter, Route, Routes } from "react-router-dom"; +import { Link, MemoryRouter, Route, Routes } from "react-router"; import { describe, expect, test } from "vitest"; import { act, render } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -8,14 +8,8 @@ import { banner, Banner } from "."; const wrapper = ({ children }: { children: ReactNode }) => ( - } - /> - } - /> + } /> + } /> {children} @@ -104,7 +98,9 @@ describe("banner", () => { }); }); - await user.click(container.querySelector("#dashboard-link")); + const dashboardLink = container.querySelector("#dashboard-link"); + expect(dashboardLink).toBeTruthy(); + if (dashboardLink) await user.click(dashboardLink); expect(queryByTestId("banner-header")).not.toBeInTheDocument(); }); diff --git a/react-app/src/components/Banner/banner.tsx b/react-app/src/components/Banner/banner.tsx index 48adf4c858..68103c4f59 100644 --- a/react-app/src/components/Banner/banner.tsx +++ b/react-app/src/components/Banner/banner.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { Alert, AlertVariant } from "../Alert"; import { Check, X } from "lucide-react"; -import { useLocation } from "react-router-dom"; +import { useLocation } from "react-router"; import { Observer } from "@/utils/basic-observable"; export type Banner = { @@ -57,10 +57,7 @@ export const Banner = () => { if (activeBanner && activeBanner.pathnameToDisplayOn === pathname) { return ( - +
diff --git a/react-app/src/components/BreadCrumb/BreadCrumb.test.tsx b/react-app/src/components/BreadCrumb/BreadCrumb.test.tsx index 691aa7c2de..76c381a0c9 100644 --- a/react-app/src/components/BreadCrumb/BreadCrumb.test.tsx +++ b/react-app/src/components/BreadCrumb/BreadCrumb.test.tsx @@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach } from "vitest"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { BreadCrumb, BreadCrumbBar, BreadCrumbs } from "./BreadCrumb"; -import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom"; +import { BrowserRouter, Route, Routes, useLocation } from "react-router"; import { optionCrumbsFromPath } from "./create-breadcrumbs"; import { Authority } from "shared-types"; diff --git a/react-app/src/components/BreadCrumb/BreadCrumb.tsx b/react-app/src/components/BreadCrumb/BreadCrumb.tsx index 8acee4c708..cfca4089c8 100644 --- a/react-app/src/components/BreadCrumb/BreadCrumb.tsx +++ b/react-app/src/components/BreadCrumb/BreadCrumb.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { type ReactNode } from "react"; import { ChevronRight } from "lucide-react"; @@ -29,11 +29,7 @@ export const BreadCrumbs = ({ options }: BreadCrumbsProps) => { .toSorted((option, prevOption) => option.order - prevOption.order) .map(({ displayText, to }, index, optionsArray) => { return ( - + {displayText} ); @@ -78,11 +74,7 @@ export const BreadCrumbSeperator = () => ; export const BreadCrumbBar = ({ children }: React.PropsWithChildren) => { return ( -
diff --git a/react-app/src/components/Form/content/ContentWrappers.tsx b/react-app/src/components/Form/content/ContentWrappers.tsx index 5befda867f..b4f0743cb3 100644 --- a/react-app/src/components/Form/content/ContentWrappers.tsx +++ b/react-app/src/components/Form/content/ContentWrappers.tsx @@ -57,7 +57,7 @@ export const ActionFormDescription = ({ }; export const ActionFormHeading = ({ title }: { title: string }) => { - return

{title}

; + return

{title}

; }; export const PreSubmitNotice = ({ diff --git a/react-app/src/components/Form/content/FormLoadingSpinner.tsx b/react-app/src/components/Form/content/FormLoadingSpinner.tsx deleted file mode 100644 index 06979225fd..0000000000 --- a/react-app/src/components/Form/content/FormLoadingSpinner.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { useNavigation } from "react-router-dom"; -import { useFormContext } from "react-hook-form"; -import { LoadingSpinner } from "@/components"; - -export const FormLoadingSpinner = () => { - const { state } = useNavigation(); - const { formState } = useFormContext(); - return ( - (state === "submitting" || formState.isSubmitting) && - ); -}; diff --git a/react-app/src/components/Form/content/PackageSection.tsx b/react-app/src/components/Form/content/PackageSection.tsx index 728adaf410..ee1640d3f9 100644 --- a/react-app/src/components/Form/content/PackageSection.tsx +++ b/react-app/src/components/Form/content/PackageSection.tsx @@ -1,5 +1,5 @@ import { BLANK_VALUE } from "@/consts"; -import { useParams } from "react-router-dom"; +import { useParams } from "react-router"; import { AuthorityUnion } from "shared-types"; export const getIdLabel = (authority: AuthorityUnion) => { diff --git a/react-app/src/components/Form/content/index.ts b/react-app/src/components/Form/content/index.ts index d6f49893fb..77eae70168 100644 --- a/react-app/src/components/Form/content/index.ts +++ b/react-app/src/components/Form/content/index.ts @@ -1,3 +1,2 @@ export * from "./ContentWrappers"; export * from "./PackageSection"; -export * from "./FormLoadingSpinner"; diff --git a/react-app/src/components/Form/fields/AdditionalInfoSection.tsx b/react-app/src/components/Form/fields/AdditionalInfoSection.tsx deleted file mode 100644 index 6e27f27cf1..0000000000 --- a/react-app/src/components/Form/fields/AdditionalInfoSection.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { ReactNode } from "react"; -import { useFormContext } from "react-hook-form"; -import { - FormDescription, - FormField, - FormItem, - FormLabel, - Textarea, - FormSectionCard, -} from "@/components"; - -export const AdditionalInfoSection = ({ - instruction, - required, -}: { - instruction?: ReactNode; - required?: boolean; -}) => { - const form = useFormContext(); - return ( - - ( - - {instruction && ( - - {instruction} - - )} -