From add4ddccb1f6e83932a3db853bf7ed400f4975a7 Mon Sep 17 00:00:00 2001 From: Jason Lin <98117700+JasonLin0991@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:57:04 -0500 Subject: [PATCH 01/24] MCR-3758: Update launch darkly and fix testing (#2207) * Update `launchdarkly-react-client-sdk` * Implement LDProvider to renderWithProviders and update tests. * Update document on new testing approach. * Fix react key prop error. * Use ldClient configuration instead of bootstrap and update docs. * Cleanup code and fix docs wording. * Update code snippet --- .../launch-darkly-testing-approach.md | 122 ++++++++++++------ services/app-web/package.json | 2 +- .../ContactsSummarySection.tsx | 1 + .../ContractDetailsSummarySection.test.tsx | 40 +++--- .../SingleRateSummarySection.test.tsx | 19 ++- .../app-web/src/pages/App/AppBody.test.tsx | 36 +++--- .../app-web/src/pages/App/AppRoutes.test.tsx | 25 ++-- .../src/pages/Landing/Landing.test.tsx | 13 +- .../QuestionResponse.test.tsx | 33 +++-- .../UploadQuestions/UploadQuestions.test.tsx | 29 +++-- .../UploadResponse/UploadResponse.test.tsx | 26 ++-- .../ContractDetails/ContractDetails.test.tsx | 35 ++--- .../SubmissionSideNav.test.tsx | 18 ++- .../RateSummary/RateSummary.test.tsx | 49 +++---- .../SubmissionSummary.test.tsx | 11 +- services/app-web/src/testHelpers/index.ts | 1 - .../app-web/src/testHelpers/jestHelpers.tsx | 114 ++++++++-------- yarn.lock | 19 ++- 18 files changed, 339 insertions(+), 254 deletions(-) diff --git a/docs/technical-design/launch-darkly-testing-approach.md b/docs/technical-design/launch-darkly-testing-approach.md index f697fb47ed..abd1c8a423 100644 --- a/docs/technical-design/launch-darkly-testing-approach.md +++ b/docs/technical-design/launch-darkly-testing-approach.md @@ -23,60 +23,98 @@ export LD_SDK_KEY='Place Launch Darkly SDK key here' ## Feature flag unit testing ### Client side unit testing -Client side unit testing utilizes `jest.spyOn()` to mock the LaunchDarkly `useLDClient` hook and return default flag values or values specified. This implementation is done in our jest helper function `ldUseClientSpy()` located in `app-web/src/testHelpers/jestHelpers.tsx`. +Client side unit testing utilizes the `LDProvider`, from `launchdarkly-react-client-sdk`, in our [renderWithProviders](../../services/app-web/src/testHelpers/jestHelpers.tsx) function to set up feature flags for each of the tests. Currently, LaunchDarkly does not provide an actual mock `LDProvider` so if or when they do, then we could update this with that provider. -`ldUseClientSpy` takes in an object of feature flags and values as an argument. You can configure multiple flags with the object passed into `ldUseClientSpy`. +We use this method for testing because the official documented [unit testing](https://docs.launchdarkly.com/guides/sdk/unit-tests/?q=unit+test) method by LaunchDarkly does not work with our LaunchDarkly implementation. Our implementation follow exactly the documentation, so it could be how we are setting up our unit tests. Previously we had used `jest.spyOn` to intercept `useLDClient` and mock the `useLDClient.variation()` function with our defined feature flag values. With `launchdarkly-react-client-sdk@3.0.10` that method did not work anymore. -```javascript -ldUseClientSpy({ - 'rates-across-submissions': true, - 'rate-cert-assurance': true, -}) -``` +#### Configuration +When using the `LDProvider` we need to pass in a mocked `ldClient` in the configuration. This allows us to initialize `ldClient` outside of the provider, which would have required the provider to perform an API call to LaunchDarkly. Now that this API call does not happen it isolates our unit tests from the feature flag values on the LaunchDarkly server and only use the values we define in each test. -To configure feature flags for a single test place `ldUseClientSpy` at the beginning of your test. +The configuration below, in `renderWithProviders`, the `ldClient` field is how we initialize `ldClient` with our defined flag values. We are using the `ldClientMock()` function to generate a mock that matches the type this field requires. -```javascript -it('cannot continue if no documents are added to the second rate', async () => { - ldUseClientSpy({ 'rates-across-submissions': true }) - const mockUpdateDraftFn = jest.fn() - renderWithProviders( - , - { - apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], - }, - } - ) +You will also see that, compared to our configuration in [app-web/src/index.tsx](../../services/app-web/src/index.tsx), the config needed to connect to LaunchDarkly is replaced with `test-url`. - ... -}) +```typescript +const ldProviderConfig: ProviderConfig = { + clientSideID: 'test-url', + options: { + bootstrap: flags, + baseUrl: 'test-url', + streamUrl: 'test-url', + eventsUrl: 'test-url', + }, + ldClient: ldClientMock(flags) +} ``` -To configure multiple tests inside a `describe` block you can: -- Follow the method for single test on each test inside the `describe`. -- If all the tests require the same flag configuration place `ldUseClientSpy` at the top of the block in `beforeEach()`. +The two important functions in the `ldCientMock` are `variation` and `allFlags`. These two functions are the ones we use in the app to get feature flags and here we are mocking them with the flag values we define in each test. If we need any other functions in `ldClient` we would just add the mock to `ldClientMock()`. ```javascript -describe('rates across submissions', () => { - beforeEach(() => - ldUseClientSpy({ - 'rates-across-submissions': true, - }) - ) - afterEach(() => { - jest.clearAllMocks() - }) - - ... +const ldClientMock = (featureFlags: FeatureFlagSettings): LDClient => ({ + ... other functions, + variation: jest.fn( + ( + flag: FeatureFlagLDConstant, + defaultValue: FlagValue | undefined + ) => featureFlags[flag] ?? defaultValue + ), + allFlags: jest.fn(() => featureFlags), }) ``` -It's always best to `jest.clearAllMocks()` after each test with either one of these methods, otherwise, preceding tests may have the same flag configured as the previous test. +We define our initial feature flag values in the `flags` variable by combining the default feature flag values with values passed into `renderWithProviders` for each test. Looking at the code snippet below from `renderWithProviders`, we get the default flag values from [flags.ts](../../services/app-web/src/common-code/featureFlags/flags.ts) using `getDefaultFeatureFlags()` then merge that with `option.featureFlags` values passed into `renderWithProviders`. This will allow each test to configure the specific feature flag values for that test and supply default values for flags the test did not define. + +```typescript +const { + routerProvider = {}, + apolloProvider = {}, + authProvider = {}, + s3Provider = undefined, + location = undefined, + featureFlags = undefined +} = options || {} + +const flags = { + ...getDefaultFeatureFlags(), + ...featureFlags +} + +const ldProviderConfig: ProviderConfig = { + clientSideID: 'test-url', + options: { + bootstrap: flags, + baseUrl: 'test-url', + streamUrl: 'test-url', + eventsUrl: 'test-url', + }, + ldClient: ldClientMock(flags) +} +``` + +#### Examples + +Using this method in our unit tests is simple and similar to how we configure the other providers. When calling `renderWithProdivers` we need to supply the second argument `options` with the `featureFlag` field. + +In the example below we set `featureFlag` with an object that contains two feature flags and their values. When this test is run, the component will be supplied with these two flag values along with the other default flag values from [flags.ts](../../services/app-web/src/common-code/featureFlags/flags.ts). Take note that the `featureFlag` field is type `FeatureFlagSettings` so you will only be allowed to define flags that exists in [flags.ts](../../services/app-web/src/common-code/featureFlags/flags.ts). + +```javascript +renderWithProviders( + , + { + apolloProvider: { + mocks: [fetchCurrentUserMock({ statusCode: 200 })], + }, + featureFlags: { + 'rate-edit-unlock': false, + '438-attestation': true + } + } +) +``` ### Server side unit testing LaunchDarkly server side implementation is done by configuring our resolvers with `ldService` dependency. In our resolver we then can use the method `getFeatureFlag` from `ldService` to get the flag value from LaunchDarkly. diff --git a/services/app-web/package.json b/services/app-web/package.json index b0f313b8b2..a4299ec662 100644 --- a/services/app-web/package.json +++ b/services/app-web/package.json @@ -105,7 +105,7 @@ "graphql": "^16.2.0", "jotai": "^2.2.1", "jotai-location": "^0.5.1", - "launchdarkly-react-client-sdk": "^3.0.1", + "launchdarkly-react-client-sdk": "^3.0.10", "path-browserify": "^1.0.1", "qs": "^6.11.0", "react": "^18.2.0", diff --git a/services/app-web/src/components/SubmissionSummarySection/ContactsSummarySection/ContactsSummarySection.tsx b/services/app-web/src/components/SubmissionSummarySection/ContactsSummarySection/ContactsSummarySection.tsx index 8a721a18bd..05a3cb7714 100644 --- a/services/app-web/src/components/SubmissionSummarySection/ContactsSummarySection/ContactsSummarySection.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/ContactsSummarySection/ContactsSummarySection.tsx @@ -51,6 +51,7 @@ export const ContactsSummarySection = ({ submission.stateContacts.map( (stateContact, index) => ( { - afterEach(() => { - jest.restoreAllMocks() - }) const defaultApolloMocks = { mocks: [fetchCurrentUserMock({ statusCode: 200 })], } @@ -110,8 +104,7 @@ describe('ContractDetailsSummarySection', () => { }) }) - it('can render all contract details fields', () => { - ldUseClientSpy({ '438-attestation': true }) + it('can render all contract details fields', async () => { const submission = mockContractAndRatesDraft({ statutoryRegulatoryAttestation: true, }) @@ -124,14 +117,18 @@ describe('ContractDetailsSummarySection', () => { />, { apolloProvider: defaultApolloMocks, + featureFlags: { '438-attestation': true }, } ) - expect( - screen.getByRole('definition', { - name: StatutoryRegulatoryAttestationQuestion, - }) - ).toBeInTheDocument() + await waitFor(() => { + expect( + screen.getByRole('definition', { + name: StatutoryRegulatoryAttestationQuestion, + }) + ).toBeInTheDocument() + }) + expect( screen.getByRole('definition', { name: 'Contract status' }) ).toBeInTheDocument() @@ -161,7 +158,6 @@ describe('ContractDetailsSummarySection', () => { }) it('displays correct contract 438 attestation yes and no text and description', async () => { - ldUseClientSpy({ '438-attestation': true }) const submission = mockContractAndRatesDraft({ statutoryRegulatoryAttestation: false, statutoryRegulatoryAttestationDescription: 'No compliance', @@ -175,14 +171,18 @@ describe('ContractDetailsSummarySection', () => { />, { apolloProvider: defaultApolloMocks, + featureFlags: { '438-attestation': true }, } ) - expect( - screen.getByRole('definition', { - name: StatutoryRegulatoryAttestationQuestion, - }) - ).toBeInTheDocument() + await waitFor(() => { + expect( + screen.getByRole('definition', { + name: StatutoryRegulatoryAttestationQuestion, + }) + ).toBeInTheDocument() + }) + expect( screen.getByRole('definition', { name: 'Non-compliance description', diff --git a/services/app-web/src/components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection.test.tsx b/services/app-web/src/components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection.test.tsx index 5ad5218f7b..15697941ab 100644 --- a/services/app-web/src/components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection.test.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection.test.tsx @@ -1,7 +1,4 @@ -import { - ldUseClientSpy, - renderWithProviders, -} from '../../../testHelpers/jestHelpers' +import { renderWithProviders } from '../../../testHelpers/jestHelpers' import { SingleRateSummarySection } from './SingleRateSummarySection' import { fetchCurrentUserMock, @@ -14,13 +11,6 @@ import { packageName } from '../../../common-code/healthPlanFormDataType' import { RateRevision } from '../../../gen/gqlClient' describe('SingleRateSummarySection', () => { - beforeEach(() => { - ldUseClientSpy({ 'rate-edit-unlock': true }) - }) - afterEach(() => { - jest.resetAllMocks() - }) - it('can render rate details without errors', async () => { const rateData = rateDataMock() await waitFor(() => { @@ -39,6 +29,7 @@ describe('SingleRateSummarySection', () => { }), ], }, + featureFlags: { 'rate-edit-unlock': true }, } ) }) @@ -134,6 +125,7 @@ describe('SingleRateSummarySection', () => { }), ], }, + featureFlags: { 'rate-edit-unlock': true }, } ) }) @@ -234,6 +226,7 @@ describe('SingleRateSummarySection', () => { }), ], }, + featureFlags: { 'rate-edit-unlock': true }, } ) @@ -273,6 +266,7 @@ describe('SingleRateSummarySection', () => { }), ], }, + featureFlags: { 'rate-edit-unlock': true }, } ) @@ -313,6 +307,7 @@ describe('SingleRateSummarySection', () => { }), ], }, + featureFlags: { 'rate-edit-unlock': true }, } ) expect( @@ -345,6 +340,7 @@ describe('SingleRateSummarySection', () => { }), ], }, + featureFlags: { 'rate-edit-unlock': true }, } ) await waitFor(() => { @@ -379,6 +375,7 @@ describe('SingleRateSummarySection', () => { }), ], }, + featureFlags: { 'rate-edit-unlock': true }, } ) expect( diff --git a/services/app-web/src/pages/App/AppBody.test.tsx b/services/app-web/src/pages/App/AppBody.test.tsx index 9968b35577..2947038058 100644 --- a/services/app-web/src/pages/App/AppBody.test.tsx +++ b/services/app-web/src/pages/App/AppBody.test.tsx @@ -1,7 +1,6 @@ import { screen } from '@testing-library/react' import { - ldUseClientSpy, renderWithProviders, userClickSignIn, } from '../../testHelpers/jestHelpers' @@ -26,16 +25,18 @@ describe('AppBody', () => { }) it('App renders without errors', () => { - ldUseClientSpy({ 'session-expiring-modal': false }) - renderWithProviders() + renderWithProviders(, { + featureFlags: { 'session-expiring-modal': false }, + }) const mainElement = screen.getByRole('main') expect(mainElement).toBeInTheDocument() }) describe('Sign In buttton click', () => { it('displays local login heading when expected', async () => { - ldUseClientSpy({}) - renderWithProviders() + renderWithProviders(, { + featureFlags: { 'session-expiring-modal': false }, + }) await userClickSignIn(screen) @@ -48,8 +49,9 @@ describe('AppBody', () => { }) it('displays Cognito login page when expected', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) - renderWithProviders() + renderWithProviders(, { + featureFlags: { 'session-expiring-modal': false }, + }) await userClickSignIn(screen) expect( @@ -58,8 +60,9 @@ describe('AppBody', () => { }) it('displays Cognito signup page when expected', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) - renderWithProviders() + renderWithProviders(, { + featureFlags: { 'session-expiring-modal': false }, + }) await userClickSignIn(screen) expect( @@ -88,7 +91,6 @@ describe('AppBody', () => { it('shows test environment banner in val', () => { process.env.REACT_APP_STAGE_NAME = 'val' - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { apolloProvider: { mocks: [ @@ -96,6 +98,7 @@ describe('AppBody', () => { indexHealthPlanPackagesMockSuccess(), ], }, + featureFlags: { 'session-expiring-modal': false }, }) expect( @@ -105,7 +108,6 @@ describe('AppBody', () => { it('does not show test environment banner in prod', () => { process.env.REACT_APP_STAGE_NAME = 'prod' - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { apolloProvider: { mocks: [ @@ -113,6 +115,7 @@ describe('AppBody', () => { indexHealthPlanPackagesMockSuccess(), ], }, + featureFlags: { 'session-expiring-modal': false }, }) expect(screen.queryByText('THIS IS A TEST ENVIRONMENT')).toBeNull() @@ -121,10 +124,6 @@ describe('AppBody', () => { describe('Site under maintenance banner', () => { it('displays maintenance banner when feature flag is on', async () => { - ldUseClientSpy({ - 'site-under-maintenance-banner': 'UNSCHEDULED', - 'session-expiring-modal': false, - }) renderWithProviders(, { apolloProvider: { mocks: [ @@ -132,6 +131,10 @@ describe('AppBody', () => { indexHealthPlanPackagesMockSuccess(), ], }, + featureFlags: { + 'site-under-maintenance-banner': 'UNSCHEDULED', + 'session-expiring-modal': false, + }, }) expect( await screen.findByRole('heading', { name: 'Site unavailable' }) @@ -144,7 +147,6 @@ describe('AppBody', () => { }) it('does not display maintenance banner when flag is off', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { apolloProvider: { mocks: [ @@ -152,6 +154,7 @@ describe('AppBody', () => { indexHealthPlanPackagesMockSuccess(), ], }, + featureFlags: { 'session-expiring-modal': false }, }) expect( screen.queryByRole('heading', { name: 'Site Unavailable' }) @@ -166,7 +169,6 @@ describe('AppBody', () => { describe('Page scrolling', () => { it('scroll top on page load', async () => { - ldUseClientSpy({}) renderWithProviders() await userClickSignIn(screen) expect(window.scrollTo).toHaveBeenCalledWith(0, 0) diff --git a/services/app-web/src/pages/App/AppRoutes.test.tsx b/services/app-web/src/pages/App/AppRoutes.test.tsx index e0bc56c927..a9fd159262 100644 --- a/services/app-web/src/pages/App/AppRoutes.test.tsx +++ b/services/app-web/src/pages/App/AppRoutes.test.tsx @@ -1,9 +1,6 @@ import { screen, waitFor } from '@testing-library/react' -import { - ldUseClientSpy, - renderWithProviders, -} from '../../testHelpers/jestHelpers' +import { renderWithProviders } from '../../testHelpers/jestHelpers' import { AppRoutes } from './AppRoutes' import { fetchCurrentUserMock, @@ -22,7 +19,6 @@ describe('AppRoutes', () => { }) describe('/[root]', () => { it('state dashboard when state user logged in', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { apolloProvider: { mocks: [ @@ -30,6 +26,7 @@ describe('AppRoutes', () => { indexHealthPlanPackagesMockSuccess(), ], }, + featureFlags: { 'session-expiring-modal': false }, }) await waitFor(() => { @@ -46,7 +43,6 @@ describe('AppRoutes', () => { }) it('cms dashboard when cms user logged in', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { apolloProvider: { mocks: [ @@ -57,6 +53,7 @@ describe('AppRoutes', () => { indexHealthPlanPackagesMockSuccess(), ], }, + featureFlags: { 'session-expiring-modal': false }, }) await waitFor(() => { @@ -73,7 +70,6 @@ describe('AppRoutes', () => { }) it('landing page when no user', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { apolloProvider: { mocks: [ @@ -82,6 +78,7 @@ describe('AppRoutes', () => { }), ], }, + featureFlags: { 'session-expiring-modal': false }, }) await waitFor(() => { expect( @@ -102,7 +99,6 @@ describe('AppRoutes', () => { describe('/auth', () => { it('auth header is displayed', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { routerProvider: { route: '/auth' }, apolloProvider: { @@ -112,6 +108,7 @@ describe('AppRoutes', () => { }), ], }, + featureFlags: { 'session-expiring-modal': false }, }) await waitFor(() => { @@ -136,8 +133,13 @@ describe('AppRoutes', () => { renderWithProviders(, { routerProvider: { route: '/help' }, apolloProvider: { - mocks: [fetchCurrentUserMock({ statusCode: 200 })], + mocks: [ + fetchCurrentUserMock({ + statusCode: 200, + }), + ], }, + featureFlags: { 'session-expiring-modal': false }, }) await screen.findByTestId('help-authenticated') @@ -162,6 +164,7 @@ describe('AppRoutes', () => { }), ], }, + featureFlags: { 'session-expiring-modal': false }, }) await screen.findByTestId('help-authenticated') await waitFor(() => { @@ -200,7 +203,6 @@ describe('AppRoutes', () => { describe('invalid routes', () => { it('redirect to landing page when no user', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { routerProvider: { route: '/not-a-real-place' }, apolloProvider: { @@ -210,6 +212,7 @@ describe('AppRoutes', () => { }), ], }, + featureFlags: { 'session-expiring-modal': false }, }) await waitFor(() => { @@ -223,12 +226,12 @@ describe('AppRoutes', () => { }) it('redirect to 404 error page when user is logged in', async () => { - ldUseClientSpy({ 'session-expiring-modal': false }) renderWithProviders(, { apolloProvider: { mocks: [fetchCurrentUserMock({ statusCode: 200 })], }, routerProvider: { route: '/not-a-real-place' }, + featureFlags: { 'session-expiring-modal': false }, }) await waitFor(() => diff --git a/services/app-web/src/pages/Landing/Landing.test.tsx b/services/app-web/src/pages/Landing/Landing.test.tsx index 46aa0d635f..11fbbf34da 100644 --- a/services/app-web/src/pages/Landing/Landing.test.tsx +++ b/services/app-web/src/pages/Landing/Landing.test.tsx @@ -1,17 +1,16 @@ import { screen } from '@testing-library/react' -import { - ldUseClientSpy, - renderWithProviders, -} from '../../testHelpers/jestHelpers' +import { renderWithProviders } from '../../testHelpers/jestHelpers' import { Landing } from './Landing' describe('Landing', () => { afterAll(() => jest.clearAllMocks()) it('displays session expired when query parameter included', async () => { - ldUseClientSpy({ 'site-under-maintenance-banner': false }) renderWithProviders(, { routerProvider: { route: '/?session-timeout' }, + featureFlags: { + 'site-under-maintenance-banner': false, + }, }) expect( screen.queryByRole('heading', { name: 'Session expired' }) @@ -21,9 +20,11 @@ describe('Landing', () => { ).toBeNull() }) it('does not display session expired by default', async () => { - ldUseClientSpy({ 'site-under-maintenance-banner': false }) renderWithProviders(, { routerProvider: { route: '/' }, + featureFlags: { + 'site-under-maintenance-banner': false, + }, }) expect( screen.queryByRole('heading', { diff --git a/services/app-web/src/pages/QuestionResponse/QuestionResponse.test.tsx b/services/app-web/src/pages/QuestionResponse/QuestionResponse.test.tsx index aa3ebb5de6..39ce59d492 100644 --- a/services/app-web/src/pages/QuestionResponse/QuestionResponse.test.tsx +++ b/services/app-web/src/pages/QuestionResponse/QuestionResponse.test.tsx @@ -2,7 +2,7 @@ import { screen, waitFor, within } from '@testing-library/react' import { Route, Routes } from 'react-router-dom' import { SubmissionSideNav } from '../SubmissionSideNav' import { QuestionResponse } from './QuestionResponse' -import { ldUseClientSpy, renderWithProviders } from '../../testHelpers' +import { renderWithProviders } from '../../testHelpers' import { RoutesRecord } from '../../constants/routes' import { @@ -15,13 +15,6 @@ import { import { IndexQuestionsPayload } from '../../gen/gqlClient' describe('QuestionResponse', () => { - beforeEach(() => { - ldUseClientSpy({ 'cms-questions': true }) - }) - afterEach(() => { - jest.resetAllMocks() - }) - it('renders expected questions correctly with rounds', async () => { const mockQuestions = mockQuestionsPayload('15') @@ -50,6 +43,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers', }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -177,6 +173,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers', }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -231,6 +230,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers', }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -291,6 +293,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers', }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -338,6 +343,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers?submit=question', }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -372,6 +380,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers?submit=response', }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -404,6 +415,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers', }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -438,6 +452,9 @@ describe('QuestionResponse', () => { routerProvider: { route: '/submissions/15/question-and-answers', }, + featureFlags: { + 'cms-questions': true, + }, } ) diff --git a/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.test.tsx b/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.test.tsx index ad388fe0b5..07311432ad 100644 --- a/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.test.tsx +++ b/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.test.tsx @@ -4,7 +4,6 @@ import { Route, Routes } from 'react-router-dom' import { UploadQuestions } from '../../QuestionResponse' import { dragAndDrop, - ldUseClientSpy, renderWithProviders, TEST_DOC_FILE, TEST_PDF_FILE, @@ -26,13 +25,6 @@ import { SubmissionSideNav } from '../../SubmissionSideNav' import { Location } from 'react-router-dom' describe('UploadQuestions', () => { - beforeEach(() => { - ldUseClientSpy({ 'cms-questions': true }) - }) - afterEach(() => { - jest.resetAllMocks() - }) - it('displays file upload for correct cms division', async () => { const division = 'testDivision' renderWithProviders( @@ -59,6 +51,9 @@ describe('UploadQuestions', () => { routerProvider: { route: `/submissions/15/question-and-answers/${division}/upload-questions`, }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -104,6 +99,9 @@ describe('UploadQuestions', () => { routerProvider: { route: `/submissions/15/question-and-answers/dmco/upload-questions`, }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -165,6 +163,9 @@ describe('UploadQuestions', () => { route: `/submissions/15/question-and-answers/dmco/upload-questions`, }, location: (location) => (testLocation = location), + featureFlags: { + 'cms-questions': true, + }, } ) @@ -220,6 +221,9 @@ describe('UploadQuestions', () => { }), ], }, + featureFlags: { + 'cms-questions': true, + }, } ) await screen.findByRole('heading', { @@ -265,6 +269,9 @@ describe('UploadQuestions', () => { }), ], }, + featureFlags: { + 'cms-questions': true, + }, } ) await screen.findByRole('heading', { @@ -315,6 +322,9 @@ describe('UploadQuestions', () => { }), ], }, + featureFlags: { + 'cms-questions': true, + }, } ) await screen.findByRole('heading', { @@ -385,6 +395,9 @@ describe('UploadQuestions', () => { }), ], }, + featureFlags: { + 'cms-questions': true, + }, } ) diff --git a/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.test.tsx b/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.test.tsx index de8aab8017..106b143a15 100644 --- a/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.test.tsx +++ b/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.test.tsx @@ -4,7 +4,6 @@ import { Route, Routes } from 'react-router-dom' import { UploadResponse } from './UploadResponse' import { dragAndDrop, - ldUseClientSpy, renderWithProviders, TEST_DOC_FILE, TEST_PDF_FILE, @@ -24,13 +23,6 @@ import { import { SubmissionSideNav } from '../../SubmissionSideNav' describe('UploadResponse', () => { - beforeEach(() => { - ldUseClientSpy({ 'cms-questions': true }) - }) - afterEach(() => { - jest.resetAllMocks() - }) - const division = 'testDivision' const questionID = 'testQuestion' @@ -59,6 +51,9 @@ describe('UploadResponse', () => { routerProvider: { route: `/submissions/15/question-and-answers/${division}/${questionID}/upload-response`, }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -98,6 +93,9 @@ describe('UploadResponse', () => { routerProvider: { route: `/submissions/15/question-and-answers/dmco/${questionID}/upload-response`, }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -145,6 +143,9 @@ describe('UploadResponse', () => { routerProvider: { route: `/submissions/15/question-and-answers/dmco/${questionID}/upload-response`, }, + featureFlags: { + 'cms-questions': true, + }, } ) @@ -191,6 +192,9 @@ describe('UploadResponse', () => { routerProvider: { route: `/submissions/15/question-and-answers/dmco/${questionID}/upload-response`, }, + featureFlags: { + 'cms-questions': true, + }, } ) await screen.findByRole('heading', { @@ -241,6 +245,9 @@ describe('UploadResponse', () => { routerProvider: { route: `/submissions/15/question-and-answers/dmco/${questionID}/upload-response`, }, + featureFlags: { + 'cms-questions': true, + }, } ) await screen.findByRole('heading', { @@ -303,6 +310,9 @@ describe('UploadResponse', () => { routerProvider: { route: `/submissions/15/question-and-answers/dmco/${questionID}/upload-response`, }, + featureFlags: { + 'cms-questions': true, + }, } ) await screen.findByRole('heading', { diff --git a/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx b/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx index c12422ab70..fce5b2a833 100644 --- a/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx +++ b/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx @@ -17,7 +17,6 @@ import { TEST_PNG_FILE, dragAndDrop, selectYesNoRadio, - ldUseClientSpy, } from '../../../testHelpers/jestHelpers' import { ACCEPTED_SUBMISSION_FILE_TYPES } from '../../../components/FileUpload' import { ContractDetails } from './' @@ -38,10 +37,6 @@ const scrollIntoViewMock = jest.fn() HTMLElement.prototype.scrollIntoView = scrollIntoViewMock describe('ContractDetails', () => { - afterEach(() => { - jest.clearAllMocks() - }) - const emptyContractDetailsDraft = { ...mockDraft(), } @@ -1028,7 +1023,6 @@ describe('ContractDetails', () => { describe('Contract 438 attestation', () => { it('renders 438 attestation question without errors', async () => { - ldUseClientSpy({ '438-attestation': true }) const draft = mockBaseContract({ statutoryRegulatoryAttestation: true, }) @@ -1041,14 +1035,17 @@ describe('ContractDetails', () => { />, { apolloProvider: defaultApolloProvider, + featureFlags: { '438-attestation': true }, } ) }) // expect 438 attestation question to be on the page - expect( - screen.getByText(StatutoryRegulatoryAttestationQuestion) - ).toBeInTheDocument() + await waitFor(() => { + expect( + screen.getByText(StatutoryRegulatoryAttestationQuestion) + ).toBeInTheDocument() + }) const yesRadio = screen.getByRole('radio', { name: StatutoryRegulatoryAttestation.YES, @@ -1073,7 +1070,6 @@ describe('ContractDetails', () => { }) }) it('errors when continuing without answering 438 attestation question', async () => { - ldUseClientSpy({ '438-attestation': true }) const draft = mockContractAndRatesDraft({ contractDateStart: new Date('11-12-2023'), contractDateEnd: new Date('11-12-2024'), @@ -1090,14 +1086,17 @@ describe('ContractDetails', () => { />, { apolloProvider: defaultApolloProvider, + featureFlags: { '438-attestation': true }, } ) }) // expect 438 attestation question to be on the page - expect( - screen.getByText(StatutoryRegulatoryAttestationQuestion) - ).toBeInTheDocument() + await waitFor(() => { + expect( + screen.getByText(StatutoryRegulatoryAttestationQuestion) + ).toBeInTheDocument() + }) const yesRadio = screen.getByRole('radio', { name: StatutoryRegulatoryAttestation.YES, @@ -1140,7 +1139,6 @@ describe('ContractDetails', () => { }) }) it('errors when continuing without description for 438 non-compliance', async () => { - ldUseClientSpy({ '438-attestation': true }) const draft = mockContractAndRatesDraft({ contractDateStart: new Date('11-12-2023'), contractDateEnd: new Date('11-12-2024'), @@ -1157,14 +1155,17 @@ describe('ContractDetails', () => { />, { apolloProvider: defaultApolloProvider, + featureFlags: { '438-attestation': true }, } ) }) // expect 438 attestation question to be on the page - expect( - screen.getByText(StatutoryRegulatoryAttestationQuestion) - ).toBeInTheDocument() + await waitFor(() => { + expect( + screen.getByText(StatutoryRegulatoryAttestationQuestion) + ).toBeInTheDocument() + }) const continueButton = screen.getByRole('button', { name: 'Continue', diff --git a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx index 87d6aa9744..49460fdf7c 100644 --- a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx +++ b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx @@ -17,15 +17,8 @@ import { mockUnlockedHealthPlanPackage, mockValidCMSUser, } from '../../testHelpers/apolloMocks' -import { ldUseClientSpy } from '../../testHelpers' describe('SubmissionSideNav', () => { - beforeEach(() => { - ldUseClientSpy({ 'cms-questions': true }) - }) - afterEach(() => { - jest.resetAllMocks() - }) it('loads sidebar nav with expected links', async () => { renderWithProviders( @@ -55,6 +48,7 @@ describe('SubmissionSideNav', () => { routerProvider: { route: '/submissions/15', }, + featureFlags: { 'cms-questions': true }, } ) @@ -124,6 +118,7 @@ describe('SubmissionSideNav', () => { route: '/submissions/15', }, location: (location) => (testLocation = location), + featureFlags: { 'cms-questions': true }, } ) @@ -230,6 +225,7 @@ describe('SubmissionSideNav', () => { routerProvider: { route: '/submissions/15', }, + featureFlags: { 'cms-questions': true }, } ) expect( @@ -266,6 +262,7 @@ describe('SubmissionSideNav', () => { routerProvider: { route: '/submissions/15', }, + featureFlags: { 'cms-questions': true }, } ) @@ -313,6 +310,7 @@ describe('SubmissionSideNav', () => { routerProvider: { route: '/submissions/15', }, + featureFlags: { 'cms-questions': true }, } ) @@ -355,6 +353,7 @@ describe('SubmissionSideNav', () => { routerProvider: { route: '/submissions/15', }, + featureFlags: { 'cms-questions': true }, } ) @@ -398,6 +397,7 @@ describe('SubmissionSideNav', () => { route: '/submissions/15', }, location: (location) => (testLocation = location), + featureFlags: { 'cms-questions': true }, } ) @@ -443,6 +443,7 @@ describe('SubmissionSideNav', () => { route: '/submissions/15', }, location: (location) => (testLocation = location), + featureFlags: { 'cms-questions': true }, } ) @@ -487,6 +488,7 @@ describe('SubmissionSideNav', () => { routerProvider: { route: '/submissions/15', }, + featureFlags: { 'cms-questions': true }, } ) @@ -521,6 +523,7 @@ describe('SubmissionSideNav', () => { ], }, routerProvider: { route: '/submissions/404' }, + featureFlags: { 'cms-questions': true }, } ) @@ -559,6 +562,7 @@ describe('SubmissionSideNav', () => { routerProvider: { route: '/submissions/15/question-and-answers', }, + featureFlags: { 'cms-questions': true }, } ) diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx index 2d0cafc331..75ae0e088c 100644 --- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx @@ -1,5 +1,5 @@ import { screen, waitFor } from '@testing-library/react' -import { renderWithProviders, testS3Client, ldUseClientSpy } from '../../../testHelpers' +import { renderWithProviders, testS3Client } from '../../../testHelpers' import { fetchCurrentUserMock, fetchRateMockSuccess, @@ -20,8 +20,6 @@ const wrapInRoutes = (children: React.ReactNode) => { } describe('RateSummary', () => { - afterAll(() => jest.clearAllMocks()) - describe('Viewing RateSummary as a CMS user', () => { it('renders without errors', async () => { renderWithProviders(wrapInRoutes(), { @@ -38,17 +36,21 @@ describe('RateSummary', () => { route: '/rates/7a', }, }) - + expect( - await screen.findByText('Programs this rate certification covers') + await screen.findByText( + 'Programs this rate certification covers' + ) ).toBeInTheDocument() }) it('renders document download warning banner when download fails', async () => { - const error = jest.spyOn(console, 'error').mockImplementation(() => { - // mock expected console error to keep test output clear - }) - + const error = jest + .spyOn(console, 'error') + .mockImplementation(() => { + // mock expected console error to keep test output clear + }) + const s3Provider = { ...testS3Client(), getBulkDlURL: async ( @@ -73,7 +75,7 @@ describe('RateSummary', () => { }, s3Provider, }) - + await waitFor(() => { expect(screen.getByTestId('warning-alert')).toBeInTheDocument() expect(screen.getByTestId('warning-alert')).toHaveClass( @@ -101,21 +103,17 @@ describe('RateSummary', () => { route: '/rates/7a', }, }) - + const backLink = await screen.findByRole('link', { name: /Back to dashboard/, }) expect(backLink).toBeInTheDocument() - + expect(backLink).toHaveAttribute('href', '/dashboard/rate-reviews') }) }) describe('Viewing RateSummary as a State user', () => { - beforeEach(() => { - ldUseClientSpy({'rate-edit-unlock': true}) - }) - it('renders without errors', async () => { renderWithProviders(wrapInRoutes(), { apolloProvider: { @@ -128,8 +126,9 @@ describe('RateSummary', () => { ], }, routerProvider: { - route: '/rates/1337' + route: '/rates/1337', }, + featureFlags: { 'rate-edit-unlock': true }, }) await waitFor(() => { @@ -137,7 +136,9 @@ describe('RateSummary', () => { }) expect( - await screen.findByText('Programs this rate certification covers') + await screen.findByText( + 'Programs this rate certification covers' + ) ).toBeInTheDocument() }) @@ -154,13 +155,12 @@ describe('RateSummary', () => { }, //purposefully attaching invalid id to url here routerProvider: { - route: '/rates/133' + route: '/rates/133', }, + featureFlags: { 'rate-edit-unlock': true }, }) - expect( - await screen.findByText('System error') - ).toBeInTheDocument() + expect(await screen.findByText('System error')).toBeInTheDocument() }) it('renders back to dashboard link for state users', async () => { @@ -177,13 +177,14 @@ describe('RateSummary', () => { routerProvider: { route: '/rates/7a', }, + featureFlags: { 'rate-edit-unlock': true }, }) - + const backLink = await screen.findByRole('link', { name: /Back to dashboard/, }) expect(backLink).toBeInTheDocument() - + expect(backLink).toHaveAttribute('href', '/dashboard') }) }) diff --git a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx index ff487433bc..cdf49b6305 100644 --- a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx @@ -16,21 +16,12 @@ import { mockStateSubmission, mockSubmittedHealthPlanPackage, } from '../../testHelpers/apolloMocks' -import { - ldUseClientSpy, - renderWithProviders, -} from '../../testHelpers/jestHelpers' +import { renderWithProviders } from '../../testHelpers/jestHelpers' import { SubmissionSummary } from './SubmissionSummary' import { SubmissionSideNav } from '../SubmissionSideNav' import { testS3Client } from '../../testHelpers/s3Helpers' describe('SubmissionSummary', () => { - beforeEach(() => { - ldUseClientSpy({ 'cms-questions': false }) - }) - afterEach(() => { - jest.resetAllMocks() - }) it('renders without errors', async () => { renderWithProviders( diff --git a/services/app-web/src/testHelpers/index.ts b/services/app-web/src/testHelpers/index.ts index fe1c3524be..0c3e87d7c2 100644 --- a/services/app-web/src/testHelpers/index.ts +++ b/services/app-web/src/testHelpers/index.ts @@ -11,7 +11,6 @@ export { userClickByRole, userClickByTestId, userClickSignIn, - ldUseClientSpy, TEST_DOC_FILE, TEST_DOCX_FILE, TEST_PDF_FILE, diff --git a/services/app-web/src/testHelpers/jestHelpers.tsx b/services/app-web/src/testHelpers/jestHelpers.tsx index 278369d6c0..78b7d25ae7 100644 --- a/services/app-web/src/testHelpers/jestHelpers.tsx +++ b/services/app-web/src/testHelpers/jestHelpers.tsx @@ -18,7 +18,6 @@ import { PageProvider } from '../contexts/PageContext' import { S3Provider } from '../contexts/S3Context' import { testS3Client } from './s3Helpers' import { S3ClientT } from '../s3' -import * as LaunchDarkly from 'launchdarkly-react-client-sdk' import { FeatureFlagLDConstant, FlagValue, @@ -26,6 +25,35 @@ import { featureFlagKeys, featureFlags, } from '../common-code/featureFlags' +import { + LDProvider, + ProviderConfig, + LDClient, +} from 'launchdarkly-react-client-sdk' + +function ldClientMock(featureFlags: FeatureFlagSettings): LDClient { + return { + track: jest.fn(), + identify: jest.fn(), + close: jest.fn(), + flush: jest.fn(), + getContext: jest.fn(), + off: jest.fn(), + on: jest.fn(), + setStreaming: jest.fn(), + variationDetail: jest.fn(), + waitForInitialization: jest.fn(), + waitUntilGoalsReady: jest.fn(), + waitUntilReady: jest.fn(), + variation: jest.fn( + ( + flag: FeatureFlagLDConstant, + defaultValue: FlagValue | undefined + ) => featureFlags[flag] ?? defaultValue + ), + allFlags: jest.fn(() => featureFlags), + } +} /* Render */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types @@ -37,6 +65,7 @@ const renderWithProviders = ( authProvider?: Partial // used to pass user authentication state via AuthContext s3Provider?: S3ClientT // used to pass AWS S3 related state via S3Context location?: (location: Location) => Location // used to pass a location url for react-router + featureFlags?: FeatureFlagSettings } ) => { const { @@ -45,23 +74,44 @@ const renderWithProviders = ( authProvider = {}, s3Provider = undefined, location = undefined, + featureFlags = undefined, } = options || {} const { route } = routerProvider const s3Client: S3ClientT = s3Provider ?? testS3Client() const user = userEvent.setup() + const flags: FeatureFlagSettings = { + ...getDefaultFeatureFlags(), + ...featureFlags, + } + + const ldProviderConfig: ProviderConfig = { + clientSideID: 'test-url', + options: { + bootstrap: flags, + baseUrl: 'test-url', + streamUrl: 'test-url', + eventsUrl: 'test-url', + }, + ldClient: ldClientMock(flags), + } + const renderResult = render( - - - - - {location && } - {ui} - - - - + + + + + + {location && ( + + )} + {ui} + + + + + ) return { user, @@ -86,47 +136,6 @@ const getDefaultFeatureFlags = (): FeatureFlagSettings => return Object.assign(a, { [flag]: defaultValue }) }, {} as FeatureFlagSettings) -//WARNING: This required tests using this function to clear mocks afterwards. -const ldUseClientSpy = (featureFlags: FeatureFlagSettings) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - jest.spyOn(LaunchDarkly, 'useLDClient').mockImplementation((): any => { - return { - // Checks to see if flag passed into useLDClient exists in the featureFlag passed in ldUseClientSpy - // If flag passed in useLDClient does not exist, then use defaultValue that was also passed into useLDClient. - // If flag does exist the featureFlag value passed into ldUseClientSpy then use the value in featureFlag. - // - // This is done because testing components may contain more than one instance of useLDClient for a different - // flag. We do not want to apply the value passed in featureFlags to each useLDClient especially if the flag - // passed in useLDClient does not exist in featureFlags passed into ldUseClientSpy. - getUser: jest.fn(), - identify: jest.fn(), - alias: jest.fn(), - variation: ( - flag: FeatureFlagLDConstant, - defaultValue: FlagValue | undefined - ) => { - if ( - featureFlags[flag] === undefined && - defaultValue === undefined - ) { - //ldClient.variation doesn't require a default value, throwing error here if a defaultValue was not provided. - throw new Error( - 'ldUseClientSpy returned an invalid value of undefined' - ) - } - return featureFlags[flag] === undefined - ? defaultValue - : featureFlags[flag] - }, - allFlags: () => { - const defaultFeatureFlags = getDefaultFeatureFlags() - Object.assign(defaultFeatureFlags, featureFlags) - return defaultFeatureFlags - }, - } - }) -} - const prettyDebug = (label?: string, element?: HTMLElement): void => { console.info( `${label ?? 'body'}: @@ -238,7 +247,6 @@ export { userClickByRole, userClickByTestId, userClickSignIn, - ldUseClientSpy, selectYesNoRadio, TEST_DOC_FILE, TEST_DOCX_FILE, diff --git a/yarn.lock b/yarn.lock index 66cef85390..42a34ba21b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22863,10 +22863,10 @@ launchdarkly-eventsource@2.0.0: resolved "https://registry.yarnpkg.com/launchdarkly-eventsource/-/launchdarkly-eventsource-2.0.0.tgz#2832e73fa8bc0c103a7f8c6dbc3b1b53d22f9acc" integrity sha512-fxZ4IN46juAc3s8/geiutRPbI8cvUBz0Lcsayh3wfd97edYWLIsnaThw2esQ3zc6vgZ1v5IjTbdumNgoT3iRnw== -launchdarkly-js-client-sdk@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/launchdarkly-js-client-sdk/-/launchdarkly-js-client-sdk-3.1.3.tgz#e046439f0e4f0bfd6d38b9eaa4420a6e40ffc0c7" - integrity sha512-/JR/ri8z3bEj9RFTTKDjd+con4F1MsWUea1MmBDtFj4gDA0l9NDm1KzhMKiIeoBdmB2rSaeFYe4CaYOEp8IryA== +launchdarkly-js-client-sdk@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/launchdarkly-js-client-sdk/-/launchdarkly-js-client-sdk-3.1.4.tgz#e613cb53412533c07ccf140ae570fc994c59758d" + integrity sha512-yq0FeklpVuHMSRz7jfUAfyM7I/659RvGztqJ0Y9G5eN/ZrG1o2W61ZU0Nrv/gqZCtLXjarh/u1otxSFFBjTpHw== dependencies: escape-string-regexp "^4.0.0" launchdarkly-js-sdk-common "5.0.3" @@ -22880,13 +22880,13 @@ launchdarkly-js-sdk-common@5.0.3: fast-deep-equal "^2.0.1" uuid "^8.0.0" -launchdarkly-react-client-sdk@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/launchdarkly-react-client-sdk/-/launchdarkly-react-client-sdk-3.0.6.tgz#5c694a4a013757d2afb5213efd28d9c16af1595e" - integrity sha512-r7gSshScugjnJB4lJ6mAOMKpV4Pj/wUks3tsRHdfeXgER9jPdxmZOAkm0besMjK0S7lfQdjxJ2KIXrh+Mn1sQA== +launchdarkly-react-client-sdk@^3.0.10: + version "3.0.10" + resolved "https://registry.yarnpkg.com/launchdarkly-react-client-sdk/-/launchdarkly-react-client-sdk-3.0.10.tgz#33816d939d9bd18b0723c0fd30b4772f2429f3de" + integrity sha512-ssb3KWe9z42+q8X2u32OrlDntGLsv0NP/p4E2Hx4O9RU0OeFm9v6omOlIk9SMsYEQD4QzLSXAp5L3cSN2ssLlA== dependencies: hoist-non-react-statics "^3.3.2" - launchdarkly-js-client-sdk "^3.1.3" + launchdarkly-js-client-sdk "^3.1.4" lodash.camelcase "^4.3.0" lazy-ass@^1.6.0: @@ -28508,7 +28508,6 @@ serverless-plugin-scripts@^1.0.2: serverless-s3-bucket-helper@CMSgov/serverless-s3-bucket-helper: version "1.0.0" - uid "3e519d15676de237ec8ede3ff9ae26abf3f3ef0a" resolved "https://codeload.github.com/CMSgov/serverless-s3-bucket-helper/tar.gz/3e519d15676de237ec8ede3ff9ae26abf3f3ef0a" serverless-s3-local@^0.7.1: From 7e7c2e0a353acf41846a113af5d9a47c70dae2e2 Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:30:09 -0800 Subject: [PATCH 02/24] Setup Secrets for JWT (#2189) * initial pass at threading secrets for jwt * make the variable handling work again local and deployed --- .envrc | 3 +- services/app-api/serverless.yml | 4 + services/app-api/src/handlers/apollo_gql.ts | 12 ++- .../handlers/third_party_API_authorizer.ts | 20 +++- services/app-api/src/jwt/jwt.test.ts | 16 +-- services/app-api/src/jwt/jwt.ts | 6 +- .../src/resolvers/APIKey/createAPIKey.test.ts | 2 +- .../app-api/src/testHelpers/gqlHelpers.ts | 2 +- .../ActionButton/ActionButton.module.scss | 10 +- .../src/components/Banner/Banner.module.scss | 6 +- .../Breadcrumbs/Breadcrumbs.module.scss | 2 +- .../src/components/Colors/Colors.module.scss | 98 +++++++++---------- .../DataDetail/DataDetail.module.scss | 1 - .../InlineDocumentWarning.module.scss | 6 +- .../DownloadButton/DownloadButton.module.scss | 8 +- .../DynamicStepIndicator.module.scss | 3 +- .../ErrorAlert/ErrorAlert.module.scss | 10 +- .../ExpandableText/ExpandableText.module.scss | 2 +- .../FilterDateRange.module.scss | 28 +++--- .../ErrorSummary/ErrorSummary.module.scss | 5 +- .../FieldTextarea/FieldTextarea.module.scss | 2 +- .../Form/FieldYesNo/FieldYesNo.module.scss | 6 +- .../src/components/Header/Header.module.scss | 6 +- .../src/components/Logo/Logo.module.scss | 4 +- .../src/components/Modal/Modal.module.scss | 8 +- .../Modal/UnlockSubmitModal.module.scss | 3 +- .../SectionCard/SectionCard.module.scss | 10 +- .../src/components/Select/Select.module.scss | 37 +++---- .../SubmissionCard/SubmissionCard.module.scss | 3 +- .../SubmissionSummarySection.module.scss | 18 ++-- .../UploadedDocumentsTable.module.scss | 8 +- .../src/components/Tabs/Tabs.module.scss | 5 +- .../src/localAuth/LocalLogin.module.scss | 2 +- .../app-web/src/pages/App/AppBody.module.scss | 2 +- .../src/pages/Errors/Errors.module.scss | 2 +- .../GraphQLExplorer.module.scss | 14 +-- .../app-web/src/pages/Help/Help.module.scss | 3 +- .../src/pages/Landing/Landing.module.scss | 2 - .../src/pages/MccrsId/MccrsId.module.scss | 12 +-- .../QATable/QATable.module.scss | 29 +++--- .../QuestionResponse.module.scss | 47 +++++---- .../src/pages/Settings/Settings.module.scss | 12 +-- .../StateSubmissionForm.module.scss | 18 ++-- .../SubmissionRevisionSummary.module.scss | 2 +- .../SubmissionSideNav.module.scss | 28 +++--- .../RateSummary/RateSummary.tsx | 5 +- .../SubmissionSummary.module.scss | 7 +- services/app-web/src/styles/custom.scss | 11 ++- services/app-web/src/styles/mcrColors.scss | 24 ++--- services/app-web/src/styles/overrides.scss | 26 ++--- services/app-web/src/styles/theme/_color.scss | 2 - .../app-web/src/styles/uswdsSettings.scss | 1 - services/infra-api/serverless.yml | 41 +++----- 53 files changed, 321 insertions(+), 323 deletions(-) diff --git a/.envrc b/.envrc index e60feb2742..3fa8c01ecb 100644 --- a/.envrc +++ b/.envrc @@ -12,7 +12,7 @@ export CT_URL='https://cloudtamer.cms.gov/' export CT_AWS_ROLE='ct-ado-managedcare-developer-admin' export CT_IDMS='2' -# values formerly in .env (required) +# required values export SASS_PATH='src:../../node_modules' export REACT_APP_AUTH_MODE='LOCAL' export REACT_APP_STAGE_NAME='local' @@ -26,6 +26,7 @@ export DATABASE_URL='postgresql://postgres:shhhsecret@localhost:5432/postgres?sc export EMAILER_MODE='LOCAL' export LD_SDK_KEY='this-value-must-be-set-in-local' export PARAMETER_STORE_MODE='LOCAL' +export JWT_SECRET='3fd2e448ed2cec1fa46520f1b64bcb243c784f68db41ea67ef9abc45c12951d3e770162829103c439f01d2b860d06ed0da1a08895117b1ef338f1e4ed176448a' # pragma: allowlist secret export REACT_APP_OTEL_COLLECTOR_URL='http://localhost:4318/v1/traces' export REACT_APP_LD_CLIENT_ID='this-value-can-be-set-in-local-if-desired' diff --git a/services/app-api/serverless.yml b/services/app-api/serverless.yml index abb3d73f1b..76e4d5e5d0 100644 --- a/services/app-api/serverless.yml +++ b/services/app-api/serverless.yml @@ -34,6 +34,9 @@ custom: reactAppOtelCollectorUrl: ${env:REACT_APP_OTEL_COLLECTOR_URL, ssm:/configuration/react_app_otel_collector_url} dbURL: ${env:DATABASE_URL} ldSDKKey: ${env:LD_SDK_KEY, ssm:/configuration/ld_sdk_key_feds} + # because the secret is in JSON in secret manager, we have to pass it into jwtSecret when not running locally + jwtSecretJSON: ${env:CF_CONFIG_IGNORED_LOCALLY, ssm:/aws/reference/secretsmanager/api_jwt_secret_wmltestapijwtaccess} + jwtSecret: ${env:JWT_SECRET, self:custom.jwtSecretJSON.jwtsigningkey} webpack: webpackConfig: 'webpack.config.js' packager: 'yarn' @@ -154,6 +157,7 @@ provider: AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-handler OPENTELEMETRY_COLLECTOR_CONFIG_FILE: /var/task/collector.yml LD_SDK_KEY: ${self:custom.ldSDKKey} + JWT_SECRET: ${self:custom.jwtSecret} layers: prismaClientMigration: diff --git a/services/app-api/src/handlers/apollo_gql.ts b/services/app-api/src/handlers/apollo_gql.ts index 1634285734..1377e56099 100644 --- a/services/app-api/src/handlers/apollo_gql.ts +++ b/services/app-api/src/handlers/apollo_gql.ts @@ -177,6 +177,7 @@ async function initializeGQLHandler(): Promise { const otelCollectorUrl = process.env.REACT_APP_OTEL_COLLECTOR_URL const parameterStoreMode = process.env.PARAMETER_STORE_MODE const ldSDKKey = process.env.LD_SDK_KEY + const jwtSecret = process.env.JWT_SECRET // START Assert configuration is valid if (emailerMode !== 'LOCAL' && emailerMode !== 'SES') @@ -212,6 +213,13 @@ async function initializeGQLHandler(): Promise { 'Configuration Error: LD_SDK_KEY is required to run app-api.' ) } + + if (jwtSecret === undefined || jwtSecret === '') { + throw new Error( + 'Configuration Error: JWT_SECRET is required to run app-api.' + ) + } + // END const pgResult = await configurePostgres(dbURL, secretsManagerSecret) @@ -310,8 +318,8 @@ async function initializeGQLHandler(): Promise { // Hard coding this for now, next job is to run this config to this app. const jwtLib = newJWTLib({ - issuer: 'fakeIssuer', - signingKey: 'notrandom', + issuer: `mcreview-${stageName}`, + signingKey: Buffer.from(jwtSecret, 'hex'), expirationDurationS: 90 * 24 * 60 * 60, // 90 days }) diff --git a/services/app-api/src/handlers/third_party_API_authorizer.ts b/services/app-api/src/handlers/third_party_API_authorizer.ts index a3741ba476..61b0e58083 100644 --- a/services/app-api/src/handlers/third_party_API_authorizer.ts +++ b/services/app-api/src/handlers/third_party_API_authorizer.ts @@ -6,10 +6,22 @@ import type { } from 'aws-lambda' import { newJWTLib } from '../jwt' -// Hard coding this for now, next job is to run this config to this app. +const stageName = process.env.stage +const jwtSecret = process.env.JWT_SECRET + +if (stageName === undefined) { + throw new Error('Configuration Error: stage is required') +} + +if (jwtSecret === undefined || jwtSecret === '') { + throw new Error( + 'Configuration Error: JWT_SECRET is required to run app-api.' + ) +} + const jwtLib = newJWTLib({ - issuer: 'fakeIssuer', - signingKey: 'notrandom', + issuer: `mcreview-${stageName}`, + signingKey: Buffer.from(jwtSecret, 'hex'), expirationDurationS: 90 * 24 * 60 * 60, // 90 days }) @@ -19,7 +31,7 @@ export const main: APIGatewayTokenAuthorizerHandler = async ( const authToken = event.authorizationToken.replace('Bearer ', '') try { // authentication step for validating JWT token - const userId = await jwtLib.userIDFromToken(authToken) + const userId = jwtLib.userIDFromToken(authToken) if (userId instanceof Error) { const msg = 'Invalid auth token' diff --git a/services/app-api/src/jwt/jwt.test.ts b/services/app-api/src/jwt/jwt.test.ts index bdec778eee..d5eb3505d3 100644 --- a/services/app-api/src/jwt/jwt.test.ts +++ b/services/app-api/src/jwt/jwt.test.ts @@ -4,7 +4,7 @@ describe('jwtLib', () => { it('works symmetricly', () => { const jwt = newJWTLib({ issuer: 'mctest', - signingKey: 'foo', //pragma: allowlist secret + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) @@ -27,13 +27,13 @@ describe('jwtLib', () => { it('errors with wrong issuer', () => { const jwtWriter = newJWTLib({ issuer: 'wrong', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) const jwtReader = newJWTLib({ issuer: 'mctest', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) @@ -49,13 +49,13 @@ describe('jwtLib', () => { it('errors with bad expiration', () => { const jwtWriter = newJWTLib({ issuer: 'mctest', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 0, }) const jwtReader = newJWTLib({ issuer: 'mctest', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) @@ -71,13 +71,13 @@ describe('jwtLib', () => { it('errors with bad secret', () => { const jwtWriter = newJWTLib({ issuer: 'mctest', - signingKey: 'wrong', + signingKey: Buffer.from('deadbeef', 'hex'), expirationDurationS: 1000, }) const jwtReader = newJWTLib({ issuer: 'mctest', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) @@ -93,7 +93,7 @@ describe('jwtLib', () => { it('errors with bogus JWT', () => { const jwtReader = newJWTLib({ issuer: 'mctest', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) diff --git a/services/app-api/src/jwt/jwt.ts b/services/app-api/src/jwt/jwt.ts index a1da0c27fd..c817e801d0 100644 --- a/services/app-api/src/jwt/jwt.ts +++ b/services/app-api/src/jwt/jwt.ts @@ -4,7 +4,7 @@ import { sign, verify } from 'jsonwebtoken' interface JWTConfig { issuer: string - signingKey: string + signingKey: Buffer expirationDurationS: number } @@ -13,6 +13,7 @@ function createValidJWT(config: JWTConfig, userID: string): APIKeyType { subject: userID, issuer: config.issuer, expiresIn: config.expirationDurationS, + algorithm: 'HS256', // pin the default algo }) return { @@ -25,6 +26,7 @@ function userIDFromToken(config: JWTConfig, token: string): string | Error { try { const decoded = verify(token, config.signingKey, { issuer: config.issuer, + algorithms: ['HS256'], // pin the default algo }) if (!decoded.sub || typeof decoded === 'string') { @@ -45,6 +47,8 @@ interface JWTLib { function newJWTLib(config: JWTConfig): JWTLib { return { + // this is an experiment, using `curry` here, It seems clean but I'm not sure + // exactly what it's getting us yet -wml createValidJWT: curry(createValidJWT)(config), userIDFromToken: curry(userIDFromToken)(config), } diff --git a/services/app-api/src/resolvers/APIKey/createAPIKey.test.ts b/services/app-api/src/resolvers/APIKey/createAPIKey.test.ts index b6397de517..cf54dc355d 100644 --- a/services/app-api/src/resolvers/APIKey/createAPIKey.test.ts +++ b/services/app-api/src/resolvers/APIKey/createAPIKey.test.ts @@ -7,7 +7,7 @@ describe('createAPIKey', () => { it('creates a new API key', async () => { const jwt = newJWTLib({ issuer: 'mctestiss', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) diff --git a/services/app-api/src/testHelpers/gqlHelpers.ts b/services/app-api/src/testHelpers/gqlHelpers.ts index 0f07c15dfa..e2f7cc706f 100644 --- a/services/app-api/src/testHelpers/gqlHelpers.ts +++ b/services/app-api/src/testHelpers/gqlHelpers.ts @@ -91,7 +91,7 @@ const constructTestPostgresServer = async (opts?: { opts?.jwt || newJWTLib({ issuer: 'mcreviewtest', - signingKey: 'foo', + signingKey: Buffer.from('123af', 'hex'), expirationDurationS: 1000, }) diff --git a/services/app-web/src/components/ActionButton/ActionButton.module.scss b/services/app-web/src/components/ActionButton/ActionButton.module.scss index 9bd3da73f4..edaf911800 100644 --- a/services/app-web/src/components/ActionButton/ActionButton.module.scss +++ b/services/app-web/src/components/ActionButton/ActionButton.module.scss @@ -3,16 +3,16 @@ // preferred way to show a button that is disabled. avoid cursor: none .disabledCursor { - cursor: not-allowed; + cursor: not-allowed; } .buttonTextWithIcon { - vertical-align: middle; - margin-left: .5rem + vertical-align: middle; + margin-left: 0.5rem; } .buttonTextWithoutIcon { - vertical-align: middle; + vertical-align: middle; } .successButton { @@ -21,6 +21,6 @@ background-color: custom.$mcr-success-hover !important; } &:active { - background-color: custom.$mcr-success-hover !important; + background-color: custom.$mcr-success-hover !important; } } diff --git a/services/app-web/src/components/Banner/Banner.module.scss b/services/app-web/src/components/Banner/Banner.module.scss index 2392b4f24e..fa64f2cedc 100644 --- a/services/app-web/src/components/Banner/Banner.module.scss +++ b/services/app-web/src/components/Banner/Banner.module.scss @@ -2,7 +2,7 @@ @use '../../styles/uswdsImports.scss' as uswds; .bannerBodyText { - p { - margin: 0 - } + p { + margin: 0; + } } diff --git a/services/app-web/src/components/Breadcrumbs/Breadcrumbs.module.scss b/services/app-web/src/components/Breadcrumbs/Breadcrumbs.module.scss index 901034450e..41fe506a52 100644 --- a/services/app-web/src/components/Breadcrumbs/Breadcrumbs.module.scss +++ b/services/app-web/src/components/Breadcrumbs/Breadcrumbs.module.scss @@ -9,7 +9,7 @@ [class^='usa-breadcrumb'] { li:last-child a { text-decoration: none; - color: custom.$mcr-foundation-ink + color: custom.$mcr-foundation-ink; } } } diff --git a/services/app-web/src/components/Colors/Colors.module.scss b/services/app-web/src/components/Colors/Colors.module.scss index ca720e3425..33d373327a 100644 --- a/services/app-web/src/components/Colors/Colors.module.scss +++ b/services/app-web/src/components/Colors/Colors.module.scss @@ -2,55 +2,53 @@ @use '../../styles/custom.scss' as custom; :export { - mcr: { - primary: { - lighter: custom.$mcr-primary-lighter; - light: custom.$mcr-primary-light; - base: custom.$mcr-primary-base; - dark: custom.$mcr-primary-dark; - darkest: custom.$mcr-primary-darkest; - } - cmsblue: { - lightest: custom.$mcr-cmsblue-lightest; - base: custom.$mcr-cmsblue-base; - dark: custom.$mcr-cmsblue-dark; - darkest: custom.$mcr-cmsblue-darkest; - } - cyan: { - light: custom.$mcr-cyan-light; - base: custom.$mcr-cyan-base; - dark: custom.$mcr-cyan-dark; - } - gold: { - base: custom.$mcr-gold-base; - dark: custom.$mcr-gold-dark; - darker: custom.$mcr-gold-darker; - } - gray: { - dark: custom.$mcr-gray-dark; - base : custom.$mcr-gray-base; - lighter: custom.$mcr-gray-lighter; - lightest: custom.$mcr-gray-lightest; - } - foundation: { - white: custom.$mcr-foundation-white; - ink : custom.$mcr-foundation-ink; - hint: custom.$mcr-foundation-hint; - link: custom.$mcr-foundation-link; - focus: custom.$mcr-foundation-focus; - visited: custom.$mcr-foundation-visited; - - } - success: { - base: custom.$mcr-success-base; - hover: custom.$mcr-success-hover; - dark: custom.$mcr-success-dark; - } - error: { - light: custom.$mcr-error-light; - base: custom.$mcr-error-base; - dark: custom.$mcr-error-dark; + mcr: { + primary: { + lighter: custom.$mcr-primary-lighter; + light: custom.$mcr-primary-light; + base: custom.$mcr-primary-base; + dark: custom.$mcr-primary-dark; + darkest: custom.$mcr-primary-darkest; + } + cmsblue: { + lightest: custom.$mcr-cmsblue-lightest; + base: custom.$mcr-cmsblue-base; + dark: custom.$mcr-cmsblue-dark; + darkest: custom.$mcr-cmsblue-darkest; + } + cyan: { + light: custom.$mcr-cyan-light; + base: custom.$mcr-cyan-base; + dark: custom.$mcr-cyan-dark; + } + gold: { + base: custom.$mcr-gold-base; + dark: custom.$mcr-gold-dark; + darker: custom.$mcr-gold-darker; + } + gray: { + dark: custom.$mcr-gray-dark; + base: custom.$mcr-gray-base; + lighter: custom.$mcr-gray-lighter; + lightest: custom.$mcr-gray-lightest; + } + foundation: { + white: custom.$mcr-foundation-white; + ink: custom.$mcr-foundation-ink; + hint: custom.$mcr-foundation-hint; + link: custom.$mcr-foundation-link; + focus: custom.$mcr-foundation-focus; + visited: custom.$mcr-foundation-visited; + } + success: { + base: custom.$mcr-success-base; + hover: custom.$mcr-success-hover; + dark: custom.$mcr-success-dark; + } + error: { + light: custom.$mcr-error-light; + base: custom.$mcr-error-base; + dark: custom.$mcr-error-dark; + } } - } - } diff --git a/services/app-web/src/components/DataDetail/DataDetail.module.scss b/services/app-web/src/components/DataDetail/DataDetail.module.scss index c9d63838c2..7f56210cf9 100644 --- a/services/app-web/src/components/DataDetail/DataDetail.module.scss +++ b/services/app-web/src/components/DataDetail/DataDetail.module.scss @@ -17,7 +17,6 @@ margin: 0; line-height: 1.5; } - } .missingInfo { diff --git a/services/app-web/src/components/DocumentWarning/InlineDocumentWarning/InlineDocumentWarning.module.scss b/services/app-web/src/components/DocumentWarning/InlineDocumentWarning/InlineDocumentWarning.module.scss index 0ccbc70d43..48ad699876 100644 --- a/services/app-web/src/components/DocumentWarning/InlineDocumentWarning/InlineDocumentWarning.module.scss +++ b/services/app-web/src/components/DocumentWarning/InlineDocumentWarning/InlineDocumentWarning.module.scss @@ -2,7 +2,7 @@ @use '../../../styles/uswdsImports.scss' as uswds; .missingInfo { - color: custom.$mcr-gold-darker; - font-weight: 700; - display: flex; + color: custom.$mcr-gold-darker; + font-weight: 700; + display: flex; } diff --git a/services/app-web/src/components/DownloadButton/DownloadButton.module.scss b/services/app-web/src/components/DownloadButton/DownloadButton.module.scss index fe85336ec7..036ebe080f 100644 --- a/services/app-web/src/components/DownloadButton/DownloadButton.module.scss +++ b/services/app-web/src/components/DownloadButton/DownloadButton.module.scss @@ -2,14 +2,14 @@ @use '../../styles/uswdsImports.scss' as uswds; .disabledCursor { - cursor: not-allowed; + cursor: not-allowed; } .buttonTextWithIcon { - vertical-align: middle; - margin-left: .5rem + vertical-align: middle; + margin-left: 0.5rem; } .buttonTextWithoutIcon { - vertical-align: middle; + vertical-align: middle; } diff --git a/services/app-web/src/components/DynamicStepIndicator/DynamicStepIndicator.module.scss b/services/app-web/src/components/DynamicStepIndicator/DynamicStepIndicator.module.scss index 1ad0681481..c6ea7fef30 100644 --- a/services/app-web/src/components/DynamicStepIndicator/DynamicStepIndicator.module.scss +++ b/services/app-web/src/components/DynamicStepIndicator/DynamicStepIndicator.module.scss @@ -8,8 +8,7 @@ justify-content: center; } - [class^='usa-step-indicator__header'] { display: inline; } -} \ No newline at end of file +} diff --git a/services/app-web/src/components/ErrorAlert/ErrorAlert.module.scss b/services/app-web/src/components/ErrorAlert/ErrorAlert.module.scss index 28eea9aeae..5fabe03c7f 100644 --- a/services/app-web/src/components/ErrorAlert/ErrorAlert.module.scss +++ b/services/app-web/src/components/ErrorAlert/ErrorAlert.module.scss @@ -1,12 +1,12 @@ @use '../../styles/custom.scss' as custom; @use '../../styles/uswdsImports.scss' as uswds; -.messageBodyText{ - p { - margin: 0 - } +.messageBodyText { + p { + margin: 0; + } } .nowrap { - white-space: nowrap; + white-space: nowrap; } diff --git a/services/app-web/src/components/ExpandableText/ExpandableText.module.scss b/services/app-web/src/components/ExpandableText/ExpandableText.module.scss index 359d92a257..ea08f435fb 100644 --- a/services/app-web/src/components/ExpandableText/ExpandableText.module.scss +++ b/services/app-web/src/components/ExpandableText/ExpandableText.module.scss @@ -21,5 +21,5 @@ background-color: transparent; border: none; cursor: pointer; - margin-top: 0.50rem; + margin-top: 0.5rem; } diff --git a/services/app-web/src/components/FilterAccordion/FilterDateRange/FilterDateRange.module.scss b/services/app-web/src/components/FilterAccordion/FilterDateRange/FilterDateRange.module.scss index 286388fb5e..ca4ad1a0f5 100644 --- a/services/app-web/src/components/FilterAccordion/FilterDateRange/FilterDateRange.module.scss +++ b/services/app-web/src/components/FilterAccordion/FilterDateRange/FilterDateRange.module.scss @@ -2,20 +2,20 @@ @use '../../../styles/uswdsImports.scss' as uswds; .dateRangePicker { - [class='usa-label'] { - font-weight: normal; - } - [class='usa-legend'] { - font-weight: normal; - } + [class='usa-label'] { + font-weight: normal; + } + [class='usa-legend'] { + font-weight: normal; + } - [class='usa-form-group'] { - margin-top: 0rem; - width: 100%; - } + [class='usa-form-group'] { + margin-top: 0rem; + width: 100%; + } - display: grid; - grid-template-columns: repeat(2, 1fr); - column-gap: 32px; - align-items: end; + display: grid; + grid-template-columns: repeat(2, 1fr); + column-gap: 32px; + align-items: end; } diff --git a/services/app-web/src/components/Form/ErrorSummary/ErrorSummary.module.scss b/services/app-web/src/components/Form/ErrorSummary/ErrorSummary.module.scss index 561a099fcc..a4b55a7f50 100644 --- a/services/app-web/src/components/Form/ErrorSummary/ErrorSummary.module.scss +++ b/services/app-web/src/components/Form/ErrorSummary/ErrorSummary.module.scss @@ -8,7 +8,8 @@ cursor: pointer; font-weight: normal; text-align: left; - &, &:visited { + &, + &:visited { color: custom.$mcr-foundation-ink; } } @@ -22,4 +23,4 @@ padding-left: 1rem; } margin-bottom: 1rem; -} \ No newline at end of file +} diff --git a/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.module.scss b/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.module.scss index d031dcbcb2..ad94ef721c 100644 --- a/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.module.scss +++ b/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.module.scss @@ -4,4 +4,4 @@ .requiredOptionalText { display: block; color: custom.$mcr-foundation-hint; -} \ No newline at end of file +} diff --git a/services/app-web/src/components/Form/FieldYesNo/FieldYesNo.module.scss b/services/app-web/src/components/Form/FieldYesNo/FieldYesNo.module.scss index 1ae5145129..eab31d1af6 100644 --- a/services/app-web/src/components/Form/FieldYesNo/FieldYesNo.module.scss +++ b/services/app-web/src/components/Form/FieldYesNo/FieldYesNo.module.scss @@ -6,11 +6,11 @@ } .optionsContainer label { - margin-top: .8em; + margin-top: 0.8em; } .yesnofieldsecondary { - margin-top: .8em; + margin-top: 0.8em; margin-bottom: 1.5em; } @@ -25,4 +25,4 @@ .requiredOptionalText { display: block; color: custom.$mcr-foundation-hint; -} \ No newline at end of file +} diff --git a/services/app-web/src/components/Header/Header.module.scss b/services/app-web/src/components/Header/Header.module.scss index 97ca6dc9bb..a44175c323 100644 --- a/services/app-web/src/components/Header/Header.module.scss +++ b/services/app-web/src/components/Header/Header.module.scss @@ -6,7 +6,8 @@ padding: 0 uswds.units(1); color: custom.$mcr-foundation-white; - button, a { + button, + a { color: custom.$mcr-foundation-white; &:hover, @@ -21,7 +22,6 @@ padding: 0 uswds.units(1); } - .landingPageHeading { background-color: custom.$mcr-cmsblue-dark; color: custom.$mcr-foundation-white; @@ -59,7 +59,7 @@ } .dashboardHeading { - background-color: custom.$mcr-cmsblue-dark;; + background-color: custom.$mcr-cmsblue-dark; color: custom.$mcr-foundation-white; & h1 { display: flex; diff --git a/services/app-web/src/components/Logo/Logo.module.scss b/services/app-web/src/components/Logo/Logo.module.scss index 64498b737a..7f9864a696 100644 --- a/services/app-web/src/components/Logo/Logo.module.scss +++ b/services/app-web/src/components/Logo/Logo.module.scss @@ -1,4 +1,4 @@ -.override{ +.override { margin-bottom: 1rem; margin-top: 1rem; -} \ No newline at end of file +} diff --git a/services/app-web/src/components/Modal/Modal.module.scss b/services/app-web/src/components/Modal/Modal.module.scss index 01eb7c0a6e..622e5b871b 100644 --- a/services/app-web/src/components/Modal/Modal.module.scss +++ b/services/app-web/src/components/Modal/Modal.module.scss @@ -2,10 +2,10 @@ @use '../../styles/uswdsImports.scss' as uswds; .modal { - max-width: 50rem; - div { + max-width: 50rem; div { - margin: 0; + div { + margin: 0; + } } - } } diff --git a/services/app-web/src/components/Modal/UnlockSubmitModal.module.scss b/services/app-web/src/components/Modal/UnlockSubmitModal.module.scss index dfbb775b5b..6cb877323f 100644 --- a/services/app-web/src/components/Modal/UnlockSubmitModal.module.scss +++ b/services/app-web/src/components/Modal/UnlockSubmitModal.module.scss @@ -5,11 +5,10 @@ max-width: custom.$mcr-container-standard-width-fixed; } - .submitButton { background: custom.$mcr-success-base; &:hover { - background-color: custom.$mcr-success-hover !important; + background-color: custom.$mcr-success-hover !important; } } diff --git a/services/app-web/src/components/SectionCard/SectionCard.module.scss b/services/app-web/src/components/SectionCard/SectionCard.module.scss index 7970b61b72..48712cea78 100644 --- a/services/app-web/src/components/SectionCard/SectionCard.module.scss +++ b/services/app-web/src/components/SectionCard/SectionCard.module.scss @@ -1,10 +1,10 @@ @use '../../styles/custom.scss' as custom; .section { - @include custom.sectionCard; - - >h3, fieldset>h3{ - margin-top: 0; // adjust vertical space between section edge and the first heading - } + @include custom.sectionCard; + > h3, + fieldset > h3 { + margin-top: 0; // adjust vertical space between section edge and the first heading + } } diff --git a/services/app-web/src/components/Select/Select.module.scss b/services/app-web/src/components/Select/Select.module.scss index dc213cdac8..86090d3646 100644 --- a/services/app-web/src/components/Select/Select.module.scss +++ b/services/app-web/src/components/Select/Select.module.scss @@ -4,23 +4,26 @@ // react-select draws the chips in two parts, so we round the outer and square in the inner corners .multiSelect { - padding-top: 12px; - [class*='select__multi-value'] { - border-radius: 99rem 99rem 99rem 99rem; - } - [class*='select__multi-value__label'] { - background:custom.$mcr-primary-lighter; - color: custom.$mcr-foundation-ink; - border-radius: 99rem 0rem 0rem 99rem; - } - [class*='select__multi-value__remove'] { - background:custom.$mcr-primary-lighter; - color: custom.$mcr-foundation-ink; - border-radius: 0rem 99rem 99rem 0rem; + padding-top: 12px; + [class*='select__multi-value'] { + border-radius: 99rem 99rem 99rem 99rem; + } + [class*='select__multi-value__label'] { + background: custom.$mcr-primary-lighter; + color: custom.$mcr-foundation-ink; + border-radius: 99rem 0rem 0rem 99rem; + } + [class*='select__multi-value__remove'] { + background: custom.$mcr-primary-lighter; + color: custom.$mcr-foundation-ink; + border-radius: 0rem 99rem 99rem 0rem; - &:hover { - background: color.adjust(custom.$mcr-primary-lighter, $lightness: -5); - color: custom.$mcr-foundation-ink; + &:hover { + background: color.adjust( + custom.$mcr-primary-lighter, + $lightness: -5 + ); + color: custom.$mcr-foundation-ink; + } } - } } diff --git a/services/app-web/src/components/SubmissionCard/SubmissionCard.module.scss b/services/app-web/src/components/SubmissionCard/SubmissionCard.module.scss index 5f556a9ef3..99c8b1b065 100644 --- a/services/app-web/src/components/SubmissionCard/SubmissionCard.module.scss +++ b/services/app-web/src/components/SubmissionCard/SubmissionCard.module.scss @@ -1,7 +1,6 @@ @use '../../styles/custom.scss' as custom; @use '../../styles/uswdsImports.scss' as uswds; - .submissionList { padding-left: 0; } @@ -16,7 +15,7 @@ transition: background-color 200ms linear; &:hover { - background-color: custom.$mcr-gray-lightest + background-color: custom.$mcr-gray-lightest; } } diff --git a/services/app-web/src/components/SubmissionSummarySection/SubmissionSummarySection.module.scss b/services/app-web/src/components/SubmissionSummarySection/SubmissionSummarySection.module.scss index 163f33d409..e080ad1289 100644 --- a/services/app-web/src/components/SubmissionSummarySection/SubmissionSummarySection.module.scss +++ b/services/app-web/src/components/SubmissionSummarySection/SubmissionSummarySection.module.scss @@ -8,9 +8,9 @@ .summarySection { margin: uswds.units(2) auto; background: custom.$mcr-foundation-white; - padding: uswds.units(2)uswds.units(4); + padding: uswds.units(2) uswds.units(4); border: 1px solid custom.$mcr-gray-lighter; - line-height:uswds.units(3); + line-height: uswds.units(3); h2 { margin: 0; @@ -28,7 +28,7 @@ padding: 0; li { - padding:uswds.units(1) 0; + padding: uswds.units(1) 0; } } @@ -37,7 +37,7 @@ padding: 0; div { - padding-bottom:uswds.units(2); + padding-bottom: uswds.units(2); } // dont bottom pad last two grid items @@ -46,7 +46,7 @@ } } table:last-of-type { - margin-bottom:uswds.units(2); + margin-bottom: uswds.units(2); } // with nested sections, collapse bottom margin/padding for last in list @@ -57,7 +57,7 @@ } .contactInfo p { - margin:uswds.units(1) 0; + margin: uswds.units(1) 0; } .documentDesc { @@ -74,11 +74,11 @@ .rateName { display: block; font-weight: bold; - margin: 0 + margin: 0; } .certifyingActuaryDetail { - margin-bottom:uswds.units(4); + margin-bottom: uswds.units(4); @include uswds.at-media(tablet) { margin-bottom: 0; @@ -98,7 +98,7 @@ .singleColumnGrid { @include uswds.at-media(tablet) { > * { - margin-bottom:uswds.units(2); + margin-bottom: uswds.units(2); } } } diff --git a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.module.scss b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.module.scss index e916afda82..ba432ef0da 100644 --- a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.module.scss +++ b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.module.scss @@ -4,7 +4,6 @@ @mixin docs-table { width: 100%; - th { font-size: uswds.size('body', '2xs'); } @@ -22,7 +21,7 @@ } } .uploadedDocumentsTable { - @include docs-table + @include docs-table; } .supportingDocsEmpty { @@ -38,8 +37,8 @@ justify-content: space-between; } -.inlineLink{ - display: inline +.inlineLink { + display: inline; } .inlineTag { @@ -66,4 +65,3 @@ caption { margin-right: 5px; } } - diff --git a/services/app-web/src/components/Tabs/Tabs.module.scss b/services/app-web/src/components/Tabs/Tabs.module.scss index 2dfafeb352..139cd96afc 100644 --- a/services/app-web/src/components/Tabs/Tabs.module.scss +++ b/services/app-web/src/components/Tabs/Tabs.module.scss @@ -7,9 +7,7 @@ .easi-tabs { overflow: hidden; - height: 85% - - &__navigation { + height: 85% &__navigation { display: flex; justify-content: space-between; position: relative; @@ -85,5 +83,4 @@ padding: 1.5em; min-height: 500px; } - } diff --git a/services/app-web/src/localAuth/LocalLogin.module.scss b/services/app-web/src/localAuth/LocalLogin.module.scss index f078200c53..6aaf4dcae2 100644 --- a/services/app-web/src/localAuth/LocalLogin.module.scss +++ b/services/app-web/src/localAuth/LocalLogin.module.scss @@ -2,5 +2,5 @@ @use '../styles/uswdsImports.scss' as uswds; .userCard { - width: 200px; + width: 200px; } diff --git a/services/app-web/src/pages/App/AppBody.module.scss b/services/app-web/src/pages/App/AppBody.module.scss index 692e2e7d66..44655ee15e 100644 --- a/services/app-web/src/pages/App/AppBody.module.scss +++ b/services/app-web/src/pages/App/AppBody.module.scss @@ -20,5 +20,5 @@ font-weight: bold; text-align: center; font-size: 1rem; - padding: .5rem; + padding: 0.5rem; } diff --git a/services/app-web/src/pages/Errors/Errors.module.scss b/services/app-web/src/pages/Errors/Errors.module.scss index 1b895bcedc..05f547bd1f 100644 --- a/services/app-web/src/pages/Errors/Errors.module.scss +++ b/services/app-web/src/pages/Errors/Errors.module.scss @@ -11,7 +11,7 @@ align-items: center; } -.errorsFullPage{ +.errorsFullPage { flex: 1; padding: uswds.units(4) 0; } diff --git a/services/app-web/src/pages/GraphQLExplorer/GraphQLExplorer.module.scss b/services/app-web/src/pages/GraphQLExplorer/GraphQLExplorer.module.scss index 7b9e9d7ee1..19999560ee 100644 --- a/services/app-web/src/pages/GraphQLExplorer/GraphQLExplorer.module.scss +++ b/services/app-web/src/pages/GraphQLExplorer/GraphQLExplorer.module.scss @@ -2,14 +2,14 @@ @use '../../styles/uswdsImports.scss' as uswds; .background { - background-color: custom.$mcr-foundation-white; - width: 100%; - height: 100%; - display: flex; - flex: 1; - align-items: stretch; + background-color: custom.$mcr-foundation-white; + width: 100%; + height: 100%; + display: flex; + flex: 1; + align-items: stretch; } .explorer { - width: 100%; + width: 100%; } diff --git a/services/app-web/src/pages/Help/Help.module.scss b/services/app-web/src/pages/Help/Help.module.scss index bb497592f6..1ce8645c4b 100644 --- a/services/app-web/src/pages/Help/Help.module.scss +++ b/services/app-web/src/pages/Help/Help.module.scss @@ -5,7 +5,8 @@ margin: uswds.units(4) 0; table { - th, td { + th, + td { vertical-align: top; } } diff --git a/services/app-web/src/pages/Landing/Landing.module.scss b/services/app-web/src/pages/Landing/Landing.module.scss index dec3795f7a..411f7435aa 100644 --- a/services/app-web/src/pages/Landing/Landing.module.scss +++ b/services/app-web/src/pages/Landing/Landing.module.scss @@ -1,7 +1,6 @@ @use '../../styles/custom.scss' as custom; @use '../../styles/uswdsImports.scss' as uswds; - $details-text-line-height: 1.5; .detailsSection { @@ -68,6 +67,5 @@ $details-text-line-height: 1.5; margin-bottom: uswds.units(1); } } - } } diff --git a/services/app-web/src/pages/MccrsId/MccrsId.module.scss b/services/app-web/src/pages/MccrsId/MccrsId.module.scss index d7e59f3d3b..4a6ca64859 100644 --- a/services/app-web/src/pages/MccrsId/MccrsId.module.scss +++ b/services/app-web/src/pages/MccrsId/MccrsId.module.scss @@ -5,7 +5,7 @@ width: 100%; } -.mccrsIDForm { +.mccrsIDForm { [class^='usa-breadcrumb'] { min-width: 40rem; max-width: 20rem; @@ -23,7 +23,6 @@ } .formContainer.tableContainer { - &[class^='usa-form'] { max-width: 100%; width: 75rem; @@ -33,7 +32,7 @@ .formHeader { text-align: center; width: 100%; - padding:uswds.units(4) 0; + padding: uswds.units(4) 0; } .formContainer { @@ -64,11 +63,10 @@ } &[class^='usa-form'] { - min-width: 100%; max-width: 100%; - @include uswds.at-media(tablet){ + @include uswds.at-media(tablet) { min-width: 40rem; max-width: 20rem; margin: 0 auto; @@ -92,10 +90,8 @@ button { margin-top: 16px; margin-bottom: 44px; - margin-right: .25rem; + margin-right: 0.25rem; } } } - } - diff --git a/services/app-web/src/pages/QuestionResponse/QATable/QATable.module.scss b/services/app-web/src/pages/QuestionResponse/QATable/QATable.module.scss index be13ce7c01..0492d76636 100644 --- a/services/app-web/src/pages/QuestionResponse/QATable/QATable.module.scss +++ b/services/app-web/src/pages/QuestionResponse/QATable/QATable.module.scss @@ -1,22 +1,23 @@ @use '../../../styles/custom.scss' as custom; @use '../../../styles/uswdsImports.scss' as uswds; -@use '../../../components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.module.scss' as uploadedDocumentsTable; +@use '../../../components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.module.scss' + as uploadedDocumentsTable; .qaDocumentTable { - @include uploadedDocumentsTable.docs-table; - margin: 0 0 uswds.units(4) 0; + @include uploadedDocumentsTable.docs-table; + margin: 0 0 uswds.units(4) 0; } .tableHeader { - display: flex; - justify-content: space-between; - align-items: center; - padding-bottom: uswds.units(3); - h4 { - padding: 0; - margin: 0; - } - p { - margin: 0; - } + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: uswds.units(3); + h4 { + padding: 0; + margin: 0; + } + p { + margin: 0; + } } diff --git a/services/app-web/src/pages/QuestionResponse/QuestionResponse.module.scss b/services/app-web/src/pages/QuestionResponse/QuestionResponse.module.scss index ed9b83ce77..517ce6a5e8 100644 --- a/services/app-web/src/pages/QuestionResponse/QuestionResponse.module.scss +++ b/services/app-web/src/pages/QuestionResponse/QuestionResponse.module.scss @@ -2,42 +2,42 @@ @use '../../styles/uswdsImports.scss' as uswds; .background { - background-color: custom.$mcr-foundation-white; - width: 100%; + background-color: custom.$mcr-foundation-white; + width: 100%; } .container { - max-width: custom.$mcr-container-standard-width-fixed; - padding: 2rem 0; + max-width: custom.$mcr-container-standard-width-fixed; + padding: 2rem 0; } .questionSection { - margin:uswds.units(2) auto; - background: custom.$mcr-foundation-white; - padding:uswds.units(4)uswds.units(4); - border: 1px solid custom.$mcr-gray-lighter; - line-height:uswds.units(3); - width: 100%; - @include uswds.u-radius('md'); + margin: uswds.units(2) auto; + background: custom.$mcr-foundation-white; + padding: uswds.units(4) uswds.units(4); + border: 1px solid custom.$mcr-gray-lighter; + line-height: uswds.units(3); + width: 100%; + @include uswds.u-radius('md'); - h3 { - display: flex; - justify-content: space-between; - align-items: center; - padding: uswds.units(2) 0; - @include uswds.u-text('normal'); - } + h3 { + display: flex; + justify-content: space-between; + align-items: center; + padding: uswds.units(2) 0; + @include uswds.u-text('normal'); + } } .breadcrumbs { - background: none; - margin:uswds.units(2) auto; + background: none; + margin: uswds.units(2) auto; } .formContainer { > [class^='usa-fieldset'] { padding: uswds.units(4); - margin-bottom:uswds.units(2); - margin-top:uswds.units(2); + margin-bottom: uswds.units(2); + margin-top: uswds.units(2); background: custom.$mcr-foundation-white; border: 1px solid custom.$mcr-gray-lighter; @include uswds.u-radius('md'); @@ -52,11 +52,10 @@ } &[class^='usa-form'] { - min-width: 100%; max-width: 100%; - @include uswds.at-media(tablet){ + @include uswds.at-media(tablet) { min-width: 40rem; max-width: 20rem; margin: 0 auto; diff --git a/services/app-web/src/pages/Settings/Settings.module.scss b/services/app-web/src/pages/Settings/Settings.module.scss index 2a235f9848..bcd35cad68 100644 --- a/services/app-web/src/pages/Settings/Settings.module.scss +++ b/services/app-web/src/pages/Settings/Settings.module.scss @@ -2,11 +2,11 @@ @use '../../styles/uswdsImports.scss' as uswds; .table { - h2{ - font-weight: 300 + h2 { + font-weight: 300; } - thead { - th{ + thead { + th { background-color: transparent; border-top: 0; border-left: 0; @@ -25,12 +25,12 @@ .pageContainer { padding: 1em 10em; width: 100%; - background-color: custom.$mcr-foundation-white + background-color: custom.$mcr-foundation-white; } .header { text-align: left; } .wrapper { - height: 100vh; + height: 100vh; } diff --git a/services/app-web/src/pages/StateSubmission/StateSubmissionForm.module.scss b/services/app-web/src/pages/StateSubmission/StateSubmissionForm.module.scss index ff1a863598..a31760c552 100644 --- a/services/app-web/src/pages/StateSubmission/StateSubmissionForm.module.scss +++ b/services/app-web/src/pages/StateSubmission/StateSubmissionForm.module.scss @@ -1,7 +1,6 @@ @use '../../styles/custom.scss' as custom; @use '../../styles/uswdsImports.scss' as uswds; - .formPage { width: 100%; } @@ -18,7 +17,7 @@ .formHeader { text-align: center; width: 100%; - padding:uswds.units(4) 0; + padding: uswds.units(4) 0; } .formContainer { @@ -31,7 +30,7 @@ @include uswds.u-radius('md'); } // for supporting documents - >& .tableContainer { + > & .tableContainer { &[class^='usa-form'] { max-width: 100%; width: 75rem; @@ -40,7 +39,7 @@ // the first fieldset of the form sets up form container // in cases where form has multiple sub sections using SectionCard - use .withSections class > [class^='usa-fieldset']:not([class~='with-sections']) { - @include custom.sectionCard + @include custom.sectionCard; } > div[class^='usa-form-group']:not(:first-of-type) { @@ -48,11 +47,10 @@ } &[class^='usa-form'] { - min-width: 100%; max-width: 100%; - @include uswds.at-media(tablet){ + @include uswds.at-media(tablet) { min-width: 40rem; max-width: 20rem; margin: 0 auto; @@ -135,11 +133,11 @@ .legendSubHeader { font-weight: normal; &.requiredOptionalText { - margin-bottom:uswds.units(2); + margin-bottom: uswds.units(2); } } -.guidanceTextBlock{ +.guidanceTextBlock { padding-top: 0; display: flex; flex-direction: column; @@ -150,7 +148,7 @@ color: custom.$mcr-foundation-hint; } -.guidanceTextBlockNoPadding{ +.guidanceTextBlockNoPadding { display: flex; flex-direction: column; } @@ -163,7 +161,7 @@ label { max-width: none; } - div[role=note] { + div[role='note'] { margin-top: uswds.units(0); } } diff --git a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.module.scss b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.module.scss index 3fe7c73d25..af32a133ba 100644 --- a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.module.scss +++ b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.module.scss @@ -10,7 +10,7 @@ max-width: custom.$mcr-container-standard-width-fixed; padding: 2rem 0; - [id=submissionTypeSection] { + [id='submissionTypeSection'] { [class^='SectionHeader_summarySectionHeader'] { flex-direction: column; align-items: flex-start; diff --git a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.module.scss b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.module.scss index 594d728c78..41fc13c3ec 100644 --- a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.module.scss +++ b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.module.scss @@ -1,31 +1,29 @@ @use '../../styles/custom.scss' as custom; @use '../../styles/uswdsImports.scss' as uswds; - .backgroundSidebar { - background-color: custom.$mcr-foundation-white; - width: 100%; - height: 100%; - flex: 1; + background-color: custom.$mcr-foundation-white; + width: 100%; + height: 100%; + flex: 1; } .backgroundForm { - width: 100%; + width: 100%; } - .container { - @include custom.default-page-container; - display: flex; - flex-direction: row; + @include custom.default-page-container; + display: flex; + flex-direction: row; } .sideNavContainer { - padding: 2rem 0; - width: 20rem; - max-width: custom.$mcr-container-max-width-fixed; + padding: 2rem 0; + width: 20rem; + max-width: custom.$mcr-container-max-width-fixed; } .backLinkContainer { - padding-top: uswds.units(2); - padding-bottom: uswds.units(4); + padding-top: uswds.units(2); + padding-bottom: uswds.units(4); } diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx index 432096ceb3..b0555c5116 100644 --- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx +++ b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx @@ -71,7 +71,10 @@ export const RateSummary = (): React.ReactElement => { //TODO: Will have to remove this conditional along with associated loggedInUser prop once the rate dashboard //is made available to state users to={{ - pathname: loggedInUser?.__typename === 'StateUser' ? RoutesRecord.DASHBOARD : RoutesRecord.DASHBOARD_RATES, + pathname: + loggedInUser?.__typename === 'StateUser' + ? RoutesRecord.DASHBOARD + : RoutesRecord.DASHBOARD_RATES, }} > diff --git a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.module.scss b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.module.scss index 876726112b..ec7e00ac2e 100644 --- a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.module.scss +++ b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.module.scss @@ -27,7 +27,6 @@ display: inline-block; margin-left: 5px; margin-top: 0px; - } } a.editLink { @@ -42,7 +41,7 @@ .backLinkContainer { padding-top: uswds.units(2); padding-bottom: uswds.units(2); - } - .backLinkContainerNoSideBar { +} +.backLinkContainerNoSideBar { padding-bottom: uswds.units(2); - } \ No newline at end of file +} diff --git a/services/app-web/src/styles/custom.scss b/services/app-web/src/styles/custom.scss index a1e0f28f65..f2296c6e6e 100644 --- a/services/app-web/src/styles/custom.scss +++ b/services/app-web/src/styles/custom.scss @@ -9,13 +9,13 @@ @forward 'mcrColors'; // Allows access to colors anywhere custom is imported @use 'mcrColors' as *; -@use'uswdsImports.scss' as uswds; +@use 'uswdsImports.scss' as uswds; /* CONTAINERS */ // Every page starts with a flex container @mixin container { - display: flex; - flex: 1 0 auto; + display: flex; + flex: 1 0 auto; } // We have some established width limits how far page content should stretch laterally. Right now this is controlled by CSS width properties $mcr-container-standard-width-fixed: 50rem; @@ -44,7 +44,8 @@ $mcr-container-max-width-fixed: 75rem; $mcr-primary-dark, $mcr-cyan-base ); - box-shadow: inset 0 0 1px $mcr-gray-dark, + box-shadow: + inset 0 0 1px $mcr-gray-dark, 0px 0px 24px rgb(0 0 0 / 5%); } @@ -65,4 +66,4 @@ $mcr-container-max-width-fixed: 75rem; width: 1px; height: 1px; overflow: hidden; -} \ No newline at end of file +} diff --git a/services/app-web/src/styles/mcrColors.scss b/services/app-web/src/styles/mcrColors.scss index 9f44c0b857..38e1c8670f 100644 --- a/services/app-web/src/styles/mcrColors.scss +++ b/services/app-web/src/styles/mcrColors.scss @@ -6,46 +6,46 @@ */ $mcr-primary-base: #005ea2; // USWDS 'primary' -$mcr-primary-dark : #1a4480; // USWDS 'primary-dark' -$mcr-primary-darkest: #162e51; // USWDS 'primary-darker', +$mcr-primary-dark: #1a4480; // USWDS 'primary-dark' +$mcr-primary-darkest: #162e51; // USWDS 'primary-darker', $mcr-primary-light: #d9e8f6; // USWDS 'bg-primary-lighter', $mcr-primary-lighter: #e1f3f8; // USWDS 'blue-cool-5v' $mcr-cmsblue-base: #0071bc; // CMSDS 'color-primary' $mcr-cmsblue-dark: #205493; // CMSDS 'color-primary-darker $mcr-cmsblue-darkest: #112e51; // CMSDS 'color-primary-darkest' -$mcr-cmsblue-lightest: #f0fafd;// This color is slightly off from CMSDS 'color-primary-alt-lightest', don't think it maps to either system +$mcr-cmsblue-lightest: #f0fafd; // This color is slightly off from CMSDS 'color-primary-alt-lightest', don't think it maps to either system $mcr-cyan-base: #02bfe7; // CMSDS 'color-primary-alt' $mcr-cyan-dark: #009ec1; // USWDS 'cyan-40v' -$mcr-cyan-light: #99deea;// USWDS 'cyan-20' -$mcr-cyan-lighter: #e7f6F8; // USWDS 'cyan-5' +$mcr-cyan-light: #99deea; // USWDS 'cyan-20' +$mcr-cyan-lighter: #e7f6f8; // USWDS 'cyan-5' $mcr-gold-lighter: #faf3d1; //USWDS yellow-5 -$mcr-gold-light: #fee685; // USWDS yellow 10v +$mcr-gold-light: #fee685; // USWDS yellow 10v $mcr-gold-base: #ffbe2e; // USWDS 'gold-20v' $mcr-gold-dark: #e5a000; // USWDS 'gold-30v' $mcr-gold-darker: #ca9318; // CMSDS 'color-warn-darker' -$mcr-gray-base :#a9aeb1; // USWDS 'gray-cool-30' +$mcr-gray-base: #a9aeb1; // USWDS 'gray-cool-30' $mcr-gray-dark: #565c65; // USWDS 'gray-cool-60' -$mcr-gray-lighter:#dfe1e2; // USWDS 'gray-cool-10' +$mcr-gray-lighter: #dfe1e2; // USWDS 'gray-cool-10' $mcr-gray-lightest: #f0f0f0; /// USWDS 'gray-5' // mcr-foundation is used for text and containers -$mcr-foundation-ink:#1b1b1b; // USWDS 'gray-90' +$mcr-foundation-ink: #1b1b1b; // USWDS 'gray-90' $mcr-foundation-hint: #71767a; // USWDS 'text-base' $mcr-foundation-link: $mcr-primary-base; -$mcr-foundation-focus:#3e94cf; // CMS 'color-focus' +$mcr-foundation-focus: #3e94cf; // CMS 'color-focus' $mcr-foundation-white: #fff; $mcr-foundation-visited: #4c2c92; // CMS 'color-visited' // mcr-success is used for submit buttons and completed actions $mcr-success-base: #2e8540; // CMSDS 'color-success' -$mcr-success-hover:#2a7a3b; // CMSDS 'color-success-dark" // CMSDS 'color-success-dark" +$mcr-success-hover: #2a7a3b; // CMSDS 'color-success-dark" // CMSDS 'color-success-dark" $mcr-success-dark: #4d8055; // USWDS 'green-cool-50' // mcr-error is used for error, validations, and incomplete actions $mcr-error-base: #b50909; // USWDS 'red-60v' $mcr-error-dark: #981b1e; // CMS 'red-darkest -$mcr-error-light: #f4e3db; // USWDS 'red-warm-10' \ No newline at end of file +$mcr-error-light: #f4e3db; // USWDS 'red-warm-10' diff --git a/services/app-web/src/styles/overrides.scss b/services/app-web/src/styles/overrides.scss index 7f763da417..1e01ed95dc 100644 --- a/services/app-web/src/styles/overrides.scss +++ b/services/app-web/src/styles/overrides.scss @@ -8,25 +8,25 @@ @use 'mcrColors' as *; // FORM FIELDS - .usa-label, - .usa-legend { - font-weight: bold; - } +.usa-label, +.usa-legend { + font-weight: bold; +} - .usa-hint span { - display: block; - margin-top: 1rem; - } +.usa-hint span { + display: block; + margin-top: 1rem; +} - .usa-checkbox__label, - .usa-radio__label { - margin-top: 1rem; - } +.usa-checkbox__label, +.usa-radio__label { + margin-top: 1rem; +} // TOOLTIP // This can be removed removed when https://github.com/uswds/uswds/issues/4458 is fixed .usa-tooltip__body { - opacity: 0; + opacity: 0; } // BUTTONS diff --git a/services/app-web/src/styles/theme/_color.scss b/services/app-web/src/styles/theme/_color.scss index 0fcba3c1f2..f8b7e66b61 100644 --- a/services/app-web/src/styles/theme/_color.scss +++ b/services/app-web/src/styles/theme/_color.scss @@ -34,14 +34,12 @@ $theme-color-error-darker: $mcr-error-dark; $theme-color-success: $mcr-success-base; $theme-color-success-dark: $mcr-success-dark; - // Info colors $theme-color-info-dark: $mcr-cyan-dark; // Hint colors $theme-color-hint: $mcr-foundation-hint; - /* ---------------------------------------- General colors diff --git a/services/app-web/src/styles/uswdsSettings.scss b/services/app-web/src/styles/uswdsSettings.scss index d123d3ebd7..bffc0fa875 100644 --- a/services/app-web/src/styles/uswdsSettings.scss +++ b/services/app-web/src/styles/uswdsSettings.scss @@ -30,4 +30,3 @@ $theme-footer-logos-background: #f0fafd; /* MODAL */ $theme-modal-border-radius: 0; - diff --git a/services/infra-api/serverless.yml b/services/infra-api/serverless.yml index d029562458..ec8742d32b 100644 --- a/services/infra-api/serverless.yml +++ b/services/infra-api/serverless.yml @@ -224,34 +224,19 @@ resources: RoleArn: !GetAtt MetricStreamRole.Arn OutputFormat: 'opentelemetry0.7' - # AppApiGatewayAcl: - # Type: AWS::WAFv2::WebACL - # Properties: - # DefaultAction: - # Block: {} - # Rules: - # - Action: - # Allow: {} - # Name: ${sls:stage}-allow-usa-plus-territories - # Priority: 0 - # Statement: - # GeoMatchStatement: - # CountryCodes: - # - GU # Guam - # - PR # Puerto Rico - # - US # USA - # - UM # US Minor Outlying Islands - # - VI # US Virgin Islands - # - MP # Northern Mariana Islands - # VisibilityConfig: - # SampledRequestsEnabled: true - # CloudWatchMetricsEnabled: true - # MetricName: WafWebAcl - # Scope: REGIONAL - # VisibilityConfig: - # CloudWatchMetricsEnabled: true - # SampledRequestsEnabled: true - # MetricName: ${sls:stage}-webacl + JWTSecret: + Type: AWS::SecretsManager::Secret + Properties: + Name: 'api_jwt_secret_${sls:stage}' + Description: 'Dynamically generated secret for JWT signing/validation' + GenerateSecretString: + SecretStringTemplate: '{}' + GenerateStringKey: jwtsigningkey + PasswordLength: 128 + ExcludePunctuation: true + ExcludeUppercase: true + ExcludeCharacters: 'ghijklmnopqrstuvwxyz' # we want to be generating a hex string [0-9a-f] + RequireEachIncludedType: false Outputs: ApiGatewayRestApiId: From c1c7cb0258f11b92cab5ac753cb1f4afd32c21fa Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Sun, 28 Jan 2024 22:35:24 -0800 Subject: [PATCH 03/24] Fix hard coded secret name that broke dev (#2214) --- services/app-api/serverless.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/app-api/serverless.yml b/services/app-api/serverless.yml index 76e4d5e5d0..7b419b7bcc 100644 --- a/services/app-api/serverless.yml +++ b/services/app-api/serverless.yml @@ -35,7 +35,7 @@ custom: dbURL: ${env:DATABASE_URL} ldSDKKey: ${env:LD_SDK_KEY, ssm:/configuration/ld_sdk_key_feds} # because the secret is in JSON in secret manager, we have to pass it into jwtSecret when not running locally - jwtSecretJSON: ${env:CF_CONFIG_IGNORED_LOCALLY, ssm:/aws/reference/secretsmanager/api_jwt_secret_wmltestapijwtaccess} + jwtSecretJSON: ${env:CF_CONFIG_IGNORED_LOCALLY, ssm:/aws/reference/secretsmanager/api_jwt_secret_${sls:stage}} jwtSecret: ${env:JWT_SECRET, self:custom.jwtSecretJSON.jwtsigningkey} webpack: webpackConfig: 'webpack.config.js' From e645afa4375f39ddc2bf746a7078ea9e18e82428 Mon Sep 17 00:00:00 2001 From: ruizajtruss <111928238+ruizajtruss@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:41:13 -0800 Subject: [PATCH 04/24] MCR-3793: state routing for unlocked rates (#2190) * added edit rate route and constants * placeholder and routing setup * setup rerouting for :id * setup redirect * setup placeholder and some testing * renamed feature flag * updated routes * components tweaked to redirect in a manner more inline with established patterns * updated tests * adjusted for launchdarkly update * remove unused param * removed unnecessary spacing --- services/app-web/src/constants/routes.ts | 5 +- services/app-web/src/constants/tealium.ts | 1 + services/app-web/src/pages/App/AppRoutes.tsx | 11 +++- .../src/pages/RateEdit/RateEdit.test.tsx | 48 +++++++++++++++++ .../app-web/src/pages/RateEdit/RateEdit.tsx | 52 +++++++++++++++++++ .../RateSummary/RateSummary.test.tsx | 38 +++++++++++++- .../RateSummary/RateSummary.tsx | 11 ++-- 7 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 services/app-web/src/pages/RateEdit/RateEdit.test.tsx create mode 100644 services/app-web/src/pages/RateEdit/RateEdit.tsx diff --git a/services/app-web/src/constants/routes.ts b/services/app-web/src/constants/routes.ts index 22e98b4ded..747b7b8875 100644 --- a/services/app-web/src/constants/routes.ts +++ b/services/app-web/src/constants/routes.ts @@ -12,6 +12,7 @@ const ROUTES = [ 'HELP', 'SETTINGS', 'RATES_SUMMARY', + 'RATE_EDIT', 'SUBMISSIONS', 'SUBMISSIONS_NEW', 'SUBMISSIONS_TYPE', @@ -46,7 +47,8 @@ const RoutesRecord: Record = { GRAPHQL_EXPLORER: '/dev/graphql-explorer', HELP: '/help', SETTINGS: '/settings', - RATES_SUMMARY: 'rates/:id', + RATES_SUMMARY: '/rates/:id', + RATE_EDIT: '/rates/:id/edit', SUBMISSIONS: '/submissions', SUBMISSIONS_NEW: '/submissions/new', SUBMISSIONS_EDIT_TOP_LEVEL: '/submissions/:id/edit/*', @@ -122,6 +124,7 @@ const PageTitlesRecord: Record = { DASHBOARD_RATES: 'Rate review dashboard', DASHBOARD_SUBMISSIONS: 'Dashboard', RATES_SUMMARY: 'Rate summary', + RATE_EDIT: 'Edit rate', SUBMISSIONS: 'Submissions', SUBMISSIONS_NEW: 'New submission', SUBMISSIONS_EDIT_TOP_LEVEL: 'Submissions', diff --git a/services/app-web/src/constants/tealium.ts b/services/app-web/src/constants/tealium.ts index 02dbad6468..030a7659ff 100644 --- a/services/app-web/src/constants/tealium.ts +++ b/services/app-web/src/constants/tealium.ts @@ -38,6 +38,7 @@ const CONTENT_TYPE_BY_ROUTE: Record = { GRAPHQL_EXPLORER: 'dev', SETTINGS: 'table', RATES_SUMMARY: 'summary', + RATE_EDIT: 'form', SUBMISSIONS: 'form', SUBMISSIONS_NEW: 'form', SUBMISSIONS_EDIT_TOP_LEVEL: 'form', diff --git a/services/app-web/src/pages/App/AppRoutes.tsx b/services/app-web/src/pages/App/AppRoutes.tsx index 81f1b33d42..58b1a9053f 100644 --- a/services/app-web/src/pages/App/AppRoutes.tsx +++ b/services/app-web/src/pages/App/AppRoutes.tsx @@ -38,6 +38,7 @@ import { } from '../QuestionResponse' import { GraphQLExplorer } from '../GraphQLExplorer/GraphQLExplorer' import { RateSummary } from '../SubmissionSummary/RateSummary' +import { RateEdit } from '../RateEdit/RateEdit' function componentForAuthMode( authMode: AuthModeType @@ -75,7 +76,7 @@ const StateUserRoutes = ({ }): React.ReactElement => { // feature flag const ldClient = useLDClient() - const showRateSummaryPage: boolean = ldClient?.variation( + const showRatePages: boolean = ldClient?.variation( featureFlags.RATE_EDIT_UNLOCK.flag, featureFlags.RATE_EDIT_UNLOCK.defaultValue ) @@ -106,7 +107,13 @@ const StateUserRoutes = ({ path={RoutesRecord.SUBMISSIONS_NEW} element={} /> - {showRateSummaryPage && ( + {showRatePages && ( + } + /> + )} + {showRatePages && ( } diff --git a/services/app-web/src/pages/RateEdit/RateEdit.test.tsx b/services/app-web/src/pages/RateEdit/RateEdit.test.tsx new file mode 100644 index 0000000000..420d69c305 --- /dev/null +++ b/services/app-web/src/pages/RateEdit/RateEdit.test.tsx @@ -0,0 +1,48 @@ +import { screen, waitFor } from '@testing-library/react' +import { renderWithProviders } from "../../testHelpers" +import { RateEdit } from "./RateEdit" +import { fetchCurrentUserMock, fetchRateMockSuccess, mockValidStateUser } from "../../testHelpers/apolloMocks" +import { RoutesRecord } from '../../constants' +import { Route, Routes } from 'react-router-dom' + +// Wrap test component in some top level routes to allow getParams to be tested +const wrapInRoutes = (children: React.ReactNode) => { + return ( + + + + ) +} + +describe('RateEdit', () => { + afterAll(() => jest.clearAllMocks()) + + describe('Viewing RateEdit as a state user', () => { + + it('renders without errors', async () => { + renderWithProviders(wrapInRoutes(), { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidStateUser(), + statusCode: 200, + }), + fetchRateMockSuccess({ rate: { id: '1337', status: 'UNLOCKED' } }), + ], + }, + routerProvider: { + route: '/rates/1337/edit' + }, + featureFlags: { + 'rate-edit-unlock': true + } + }) + + await waitFor(() => { + expect(screen.queryByTestId('rate-edit')).toBeInTheDocument() + }) + }) + }) +}) + + diff --git a/services/app-web/src/pages/RateEdit/RateEdit.tsx b/services/app-web/src/pages/RateEdit/RateEdit.tsx new file mode 100644 index 0000000000..af04cbfb00 --- /dev/null +++ b/services/app-web/src/pages/RateEdit/RateEdit.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { useFetchRateQuery } from "../../gen/gqlClient"; +import { GridContainer } from "@trussworks/react-uswds"; +import { Loading } from "../../components"; +import { GenericErrorPage } from "../Errors/GenericErrorPage"; + +type RouteParams = { + id: string +} + +export const RateEdit = (): React.ReactElement => { + const navigate = useNavigate() + const { id } = useParams() + if (!id) { + throw new Error( + 'PROGRAMMING ERROR: id param not set in state submission form.' + ) + } + + const { data, loading, error } = useFetchRateQuery({ + variables: { + input: { + rateID: id, + }, + }, + }) + + const rate = data?.fetchRate.rate + + if (loading) { + return ( + + + + ) + } else if (error || !rate ) { + return + } + + if (rate.status !== 'UNLOCKED') { + navigate(`/rates/${id}`) + } + + return ( +

+ You've reached the '/rates/:id/edit' url placeholder for the incoming standalone edit rate form +
+ Ticket: MCR-3771 +

+ ) +} \ No newline at end of file diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx index 75ae0e088c..490f69e143 100644 --- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx @@ -9,6 +9,7 @@ import { import { RateSummary } from './RateSummary' import { RoutesRecord } from '../../../constants' import { Route, Routes } from 'react-router-dom' +import { RateEdit } from '../../RateEdit/RateEdit' // Wrap test component in some top level routes to allow getParams to be tested const wrapInRoutes = (children: React.ReactNode) => { @@ -114,7 +115,7 @@ describe('RateSummary', () => { }) describe('Viewing RateSummary as a State user', () => { - it('renders without errors', async () => { + it('renders SingleRateSummarySection component without errors for locked rate', async () => { renderWithProviders(wrapInRoutes(), { apolloProvider: { mocks: [ @@ -142,6 +143,41 @@ describe('RateSummary', () => { ).toBeInTheDocument() }) + it('redirects to RateEdit component from RateSummary without errors for unlocked rate', async () => { + renderWithProviders( + + } + /> + } + /> + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidStateUser(), + statusCode: 200, + }), + fetchRateMockSuccess({ rate: { id: '1337', status: 'UNLOCKED' } }), + ], + }, + routerProvider: { + route: '/rates/1337' + }, + featureFlags: { + 'rate-edit-unlock': true + } + }) + + await waitFor(() => { + expect(screen.queryByTestId('rate-edit')).toBeInTheDocument() + }) + }) + it('renders expected error page when rate ID is invalid', async () => { renderWithProviders(wrapInRoutes(), { apolloProvider: { diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx index b0555c5116..fc0855602f 100644 --- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx +++ b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx @@ -1,6 +1,6 @@ import { GridContainer, Icon, Link } from '@trussworks/react-uswds' import React, { useEffect, useState } from 'react' -import { NavLink, useParams } from 'react-router-dom' +import { NavLink, useNavigate, useParams } from 'react-router-dom' import { Loading } from '../../../components' import { usePage } from '../../../contexts/PageContext' @@ -19,6 +19,7 @@ export const RateSummary = (): React.ReactElement => { // Page level state const { loggedInUser } = useAuth() const { updateHeading } = usePage() + const navigate = useNavigate() const [rateName, setRateName] = useState(undefined) const { id } = useParams() if (!id) { @@ -52,6 +53,11 @@ export const RateSummary = (): React.ReactElement => { return } + //Redirecting a state user to the edit page if rate is unlocked + if (loggedInUser?.role === 'STATE_USER' && rate.status === 'UNLOCKED') { + navigate(`/rates/${id}/edit`) + } + if ( rateName !== currentRateRev.formData.rateCertificationName && currentRateRev.formData.rateCertificationName @@ -68,8 +74,7 @@ export const RateSummary = (): React.ReactElement => {
Date: Mon, 29 Jan 2024 18:57:30 -0500 Subject: [PATCH 05/24] Revert clamdscan layer PR (#2216) * PR revert JIC * sha dance * remove the dl --- .github/workflows/deploy-infra-to-env.yml | 10 - .github/workflows/deploy.yml | 25 +-- .github/workflows/promote.yml | 23 +- services/uploads/serverless.yml | 7 +- services/uploads/src/avLayer/LICENSE | 201 ------------------ services/uploads/src/avLayer/build/build.sh | 46 ---- .../uploads/src/avLayer/build/freshclam.conf | 2 - .../uploads/src/avLayer/docker-compose.yml | 9 - services/uploads/src/avLayer/dockerbuild.sh | 5 - 9 files changed, 9 insertions(+), 319 deletions(-) delete mode 100644 services/uploads/src/avLayer/LICENSE delete mode 100755 services/uploads/src/avLayer/build/build.sh delete mode 100644 services/uploads/src/avLayer/build/freshclam.conf delete mode 100644 services/uploads/src/avLayer/docker-compose.yml delete mode 100755 services/uploads/src/avLayer/dockerbuild.sh diff --git a/.github/workflows/deploy-infra-to-env.yml b/.github/workflows/deploy-infra-to-env.yml index 06e86948b3..91b9ca98cf 100644 --- a/.github/workflows/deploy-infra-to-env.yml +++ b/.github/workflows/deploy-infra-to-env.yml @@ -172,16 +172,6 @@ jobs: stage-name: ${{ inputs.stage_name }} changed-services: ${{ inputs.changed_services }} - - uses: actions/download-artifact@v3 - with: - name: lambda-layers-clamav - path: ./services/uploads/lambda-layers-clamav - - - name: Unzip clamav layer - run: | - unzip -d ./services/uploads/lambda-layers-clamav ./services/uploads/lambda-layers-clamav/lambda_layer.zip - rm -f ./services/uploads/lambda-layers-clamav/lambda_layer.zip - - name: deploy uploads id: deploy-uploads env: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bd0b2f4839..3516b53c13 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -249,30 +249,9 @@ jobs: name: lambda-layers-prisma-client-engine path: ./services/app-api/lambda-layers-prisma-client-engine - build-clamav-layer: - name: build - clamav layer - runs-on: ubuntu-20.04 - steps: - - name: Check out repository - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: yarn - - - name: Prepare ClamAV layer - working-directory: services/uploads/src/avLayer - run: ./dockerbuild.sh - - - uses: actions/upload-artifact@v3 - with: - name: lambda-layers-clamav - path: ./services/uploads/src/avLayer/build/lambda_layer.zip - deploy-infra: - needs: [begin-deployment, build-clamav-layer] - uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@main + needs: [begin-deployment] + uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@mt-revert-clamav-pr with: environment: dev stage_name: ${{ needs.begin-deployment.outputs.stage-name}} diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index 98bc3bed0f..7be91378a8 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -117,29 +117,8 @@ jobs: name: lambda-layers-prisma-client-engine path: ./services/app-api/lambda-layers-prisma-client-engine - build-clamav-layer: - name: build - clamav layer - runs-on: ubuntu-20.04 - steps: - - name: Check out repository - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: yarn - - - name: Prepare ClamAV layer - working-directory: services/uploads/src/avLayer - run: ./dockerbuild.sh - - - uses: actions/upload-artifact@v3 - with: - name: lambda-layers-clamav - path: ./services/uploads/src/avLayer/build/lambda_layer.zip - promote-infra-dev: - needs: [build-prisma-client-lambda-layer, build-clamav-layer, unit-tests] + needs: [build-prisma-client-lambda-layer, unit-tests] uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@main with: environment: dev diff --git a/services/uploads/serverless.yml b/services/uploads/serverless.yml index 5964a08a7c..0a59aee6f2 100644 --- a/services/uploads/serverless.yml +++ b/services/uploads/serverless.yml @@ -60,7 +60,11 @@ custom: scripts: hooks: # This script is run locally when running 'serverless deploy' + package:initialize: | + set -e + curl -L --output lambda_layer.zip https://github.com/CMSgov/lambda-clamav-layer/releases/download/0.7/lambda_layer.zip deploy:finalize: | + rm lambda_layer.zip serverless invoke --stage ${sls:stage} --function avDownloadDefinitions -t Event serverless-offline-ssm: stages: @@ -92,7 +96,8 @@ custom: layers: clamAv: - path: lambda-layers-clamav + package: + artifact: lambda_layer.zip functions: avScan: diff --git a/services/uploads/src/avLayer/LICENSE b/services/uploads/src/avLayer/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/services/uploads/src/avLayer/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/services/uploads/src/avLayer/build/build.sh b/services/uploads/src/avLayer/build/build.sh deleted file mode 100755 index 7e337b470e..0000000000 --- a/services/uploads/src/avLayer/build/build.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -set -e - -VERSION=${VERSION:-0.104.4-27025} -echo "prepping clamav (${VERSION})" -uname -m - -yum update -y -amazon-linux-extras install epel -y -yum install -y cpio yum-utils tar.x86_64 gzip zip - -# extract binaries for clamav, json-c, pcre -mkdir -p /tmp/build -pushd /tmp/build - -# Download other package dependencies -yumdownloader -x \*i686 --archlist=x86_64 clamav clamav-lib clamav-update clamd json-c pcre2 libtool-ltdl libxml2 bzip2-libs xz-libs libprelude gnutls nettle -rpm2cpio clamav-0*.rpm | cpio -vimd -rpm2cpio clamav-lib*.rpm | cpio -vimd -rpm2cpio clamav-update*.rpm | cpio -vimd -rpm2cpio clamd*.rpm | cpio -vimd -rpm2cpio json-c*.rpm | cpio -vimd -rpm2cpio pcre*.rpm | cpio -vimd -rpm2cpio libtool-ltdl*.rpm | cpio -vimd -rpm2cpio libxml2*.rpm | cpio -vimd -rpm2cpio bzip2-libs*.rpm | cpio -vimd -rpm2cpio xz-libs*.rpm | cpio -vimd -rpm2cpio libprelude*.rpm | cpio -vimd -rpm2cpio gnutls*.rpm | cpio -vimd -rpm2cpio nettle*.rpm | cpio -vimd - -# reset the timestamps so that we generate a reproducible zip file where -# running with the same file contents we get the exact same hash even if we -# run the same build on different days -find usr -exec touch -t 200001010000 "{}" \; -popd - -mkdir -p bin lib - -cp /tmp/build/usr/bin/clamscan /tmp/build/usr/bin/freshclam /tmp/build/usr/bin/clamdscan bin/. -cp -R /tmp/build/usr/lib64/* lib/. -cp freshclam.conf bin/freshclam.conf - -zip -r9 lambda_layer.zip bin -zip -r9 lambda_layer.zip lib \ No newline at end of file diff --git a/services/uploads/src/avLayer/build/freshclam.conf b/services/uploads/src/avLayer/build/freshclam.conf deleted file mode 100644 index b2e6fbf18e..0000000000 --- a/services/uploads/src/avLayer/build/freshclam.conf +++ /dev/null @@ -1,2 +0,0 @@ -DatabaseMirror database.clamav.net -CompressLocalDatabase yes diff --git a/services/uploads/src/avLayer/docker-compose.yml b/services/uploads/src/avLayer/docker-compose.yml deleted file mode 100644 index 51f2388b41..0000000000 --- a/services/uploads/src/avLayer/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '2' -services: - layer: - image: amazonlinux:latest - platform: linux/amd64 - working_dir: /opt/app - volumes: - - ./build:/opt/app - command: [./build.sh] diff --git a/services/uploads/src/avLayer/dockerbuild.sh b/services/uploads/src/avLayer/dockerbuild.sh deleted file mode 100755 index 5e404f3fa7..0000000000 --- a/services/uploads/src/avLayer/dockerbuild.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -e -docker pull amazonlinux:2 -docker run --rm --platform linux/x86_64 -v `pwd`/build:/opt/app amazonlinux:2 /bin/bash -c "cd /opt/app && ./build.sh" \ No newline at end of file From aab6616db3aec4bcb4b4ad88c5dd36031aaf0a1d Mon Sep 17 00:00:00 2001 From: Mojo Talantikite Date: Mon, 29 Jan 2024 20:11:20 -0500 Subject: [PATCH 06/24] move the sha (#2217) --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3516b53c13..91164a872a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -251,7 +251,7 @@ jobs: deploy-infra: needs: [begin-deployment] - uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@mt-revert-clamav-pr + uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@main with: environment: dev stage_name: ${{ needs.begin-deployment.outputs.stage-name}} From f6a6d68043dc61a68efd1815055ea8e0e044ea93 Mon Sep 17 00:00:00 2001 From: pearl-truss <67110378+pearl-truss@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:31:14 -0500 Subject: [PATCH 07/24] MCR-3836 - Modify app-api to process request from the 3rd party authorizer (#2171) * user the principalID passed back from authorizer to fetch user * simplify localAuthn to not contain 3rd party authorizer logic * resolve issue with deployed environment not having an authprovider for 3rd party requests * make sure normal userFetcher isn't called when request comes from authroizer * testing commit to check if the path exist on the event in the same way in deployed * additional output in thrown error for testing * update logic for checking if request is coming from 3rd party * pr fixes for better error message and remove duplicate code * fix typo * test awaiting for userID from jwtLib * Add documentation for third party access to API * throw error * remove changes to authn that are no longer needed * update error handling, alert with otel * resolve issue with apollo_gql expecting error returned for userfetcher --- .../third_party_api_access.md | 16 ++++++++ services/app-api/src/authn/index.ts | 1 + services/app-api/src/authn/thirdPartyAuthn.ts | 39 +++++++++++++++++++ services/app-api/src/handlers/apollo_gql.ts | 30 +++++++++++--- 4 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 docs/technical-design/third_party_api_access.md create mode 100644 services/app-api/src/authn/thirdPartyAuthn.ts diff --git a/docs/technical-design/third_party_api_access.md b/docs/technical-design/third_party_api_access.md new file mode 100644 index 0000000000..a13487aa3d --- /dev/null +++ b/docs/technical-design/third_party_api_access.md @@ -0,0 +1,16 @@ +# Third Party API Access + +## Overview +In order to make a request to the API, users (3rd party and non) need to be authenticated (signed in) and authorized (have permission to make a particular request). Non 3rd party users are both authenticated and authorized with Cognito. While 3rd party users will be able to authenticate with cognito, authorization will happen separately that we can grant them longer term credentials for authorization. + +## Process for 3rd Parties to Use the MC-Review API + +1. 3rd party is authenticated (signs on) +2. 3rd party requests a JWT +3. 3rd party uses the JWT to make a request to the MC-Review API +4. Request is sent to `v1/graphql/external` via the `graphql` API gateway endpoint, which invokes the lambda authorizer +5. The lambda authorizer, `third_party_api_authorizer`, verifies the JWT that the 3rd party sent with their request. If the JWT is valid (valid user, and not expired) the lambda returns an “allow” policy document, otherwise it returns a “deny”. This policy determines if the request can proceed. +6. When authorization is successful the user ID that was granted the JWT is used to fetch a full user record from postgres. This is user is then a part of the context for the resolver. + +## JWT Security +Like previously mentioned, third parties will need to have a valid JWT in order to access the MC-Review API. More can be found on JWT security [here](api-jwt-security.md) \ No newline at end of file diff --git a/services/app-api/src/authn/index.ts b/services/app-api/src/authn/index.ts index 485ebb656a..c7af248951 100644 --- a/services/app-api/src/authn/index.ts +++ b/services/app-api/src/authn/index.ts @@ -1,6 +1,7 @@ export type { userFromAuthProvider } from './authn' export { userFromCognitoAuthProvider, lookupUserAurora } from './cognitoAuthn' +export { userFromThirdPartyAuthorizer } from './thirdPartyAuthn' export { userFromLocalAuthProvider, diff --git a/services/app-api/src/authn/thirdPartyAuthn.ts b/services/app-api/src/authn/thirdPartyAuthn.ts new file mode 100644 index 0000000000..d8c5c8923d --- /dev/null +++ b/services/app-api/src/authn/thirdPartyAuthn.ts @@ -0,0 +1,39 @@ +import { ok, err } from 'neverthrow' +import type { Store } from '../postgres' +import { lookupUserAurora } from './cognitoAuthn' +import { initTracer, recordException } from '../../../uploads/src/lib/otel' + +export async function userFromThirdPartyAuthorizer( + store: Store, + userId: string +) { + // setup otel tracing + const otelCollectorURL = process.env.REACT_APP_OTEL_COLLECTOR_URL + if (!otelCollectorURL || otelCollectorURL === '') { + const errMsg = + 'Configuration Error: REACT_APP_OTEL_COLLECTOR_URL must be set' + throw errMsg + } + + const serviceName = 'third-party-authorizer' + initTracer(serviceName, otelCollectorURL) + + try { + // Lookup user from postgres + const auroraUser = await lookupUserAurora(store, userId) + if (auroraUser instanceof Error) { + return err(auroraUser) + } + + if (auroraUser === undefined) { + return err(auroraUser) + } + + return ok(auroraUser) + } catch (e) { + const err = new Error('ERROR: failed to look up user in postgres') + + recordException(err, serviceName, 'lookupUser') + throw err + } +} diff --git a/services/app-api/src/handlers/apollo_gql.ts b/services/app-api/src/handlers/apollo_gql.ts index 1377e56099..0e675f6f42 100644 --- a/services/app-api/src/handlers/apollo_gql.ts +++ b/services/app-api/src/handlers/apollo_gql.ts @@ -13,6 +13,7 @@ import type { userFromAuthProvider } from '../authn' import { userFromCognitoAuthProvider, userFromLocalAuthProvider, + userFromThirdPartyAuthorizer, } from '../authn' import { newLocalEmailer, newSESEmailer } from '../emailer' import { NewPostgresStore } from '../postgres/postgresStore' @@ -59,10 +60,17 @@ function contextForRequestForFetcher(userFetcher: userFromAuthProvider): ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any const anyContext = context as any const requestSpan = anyContext[requestSpanKey] - const authProvider = event.requestContext.identity.cognitoAuthenticationProvider - if (authProvider) { + // This handler is shared with the third_party_API_authorizer + // when called from the 3rd party authorizer the cognito auth provider + // is not valid for instead the authorizer returns a user ID + // that is used to fetch the user + const fromThirdPartyAuthorizer = event.requestContext.path.includes( + '/v1/graphql/external' + ) + + if (authProvider || fromThirdPartyAuthorizer) { try { // check if the user is stored in postgres // going to clean this up, but we need the store in the @@ -81,8 +89,21 @@ function contextForRequestForFetcher(userFetcher: userFromAuthProvider): ({ } const store = NewPostgresStore(pgResult) - const userResult = await userFetcher(authProvider, store) + const userId = event.requestContext.authorizer?.principalId + + let userResult + if (authProvider && !fromThirdPartyAuthorizer) { + userResult = await userFetcher(authProvider, store) + } else if (fromThirdPartyAuthorizer && userId) { + userResult = await userFromThirdPartyAuthorizer( + store, + userId + ) + } + if (userResult === undefined) { + throw new Error(`Log: userResult must be supplied`) + } if (!userResult.isErr()) { return { user: userResult.value, @@ -98,7 +119,7 @@ function contextForRequestForFetcher(userFetcher: userFromAuthProvider): ({ throw new Error('Log: placing user in gql context failed') } } else { - throw new Error('Log: no AuthProvider') + throw new Error('Log: no AuthProvider from an internal API user.') } } } @@ -132,7 +153,6 @@ function tracingMiddleware(wrapped: Handler): Handler { return async function (event, context, completion) { // get the parent context from headers const ctx = propagation.extract(ROOT_CONTEXT, event.headers) - const span = tracer.startSpan( 'handleRequest', { From 5078efd421a7387f51ed54a931f26041e3d88785 Mon Sep 17 00:00:00 2001 From: MacRae Linton <55759+macrael@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:55:03 -0800 Subject: [PATCH 08/24] Add API Key Generation Page (#2218) * initial token generation page * get api key page working --- services/app-web/src/constants/routes.ts | 3 + services/app-web/src/constants/tealium.ts | 1 + .../app-web/src/gqlHelpers/apolloErrors.ts | 2 +- services/app-web/src/index.tsx | 7 +- .../src/pages/APIAccess/APIAccess.module.scss | 22 +++ .../src/pages/APIAccess/APIAccess.test.tsx | 109 ++++++++++++ .../app-web/src/pages/APIAccess/APIAccess.tsx | 155 ++++++++++++++++++ services/app-web/src/pages/App/AppRoutes.tsx | 3 + .../src/pages/RateEdit/RateEdit.test.tsx | 73 +++++---- .../app-web/src/pages/RateEdit/RateEdit.tsx | 84 +++++----- .../SubmissionSideNav/SubmissionSideNav.tsx | 1 - .../RateSummary/RateSummary.test.tsx | 29 ++-- .../testHelpers/apolloMocks/apiKeyGQLMocks.ts | 33 ++++ .../src/testHelpers/apolloMocks/index.ts | 5 + .../thirdPartyAPIAccess.spec.ts | 78 +++++++++ 15 files changed, 513 insertions(+), 92 deletions(-) create mode 100644 services/app-web/src/pages/APIAccess/APIAccess.module.scss create mode 100644 services/app-web/src/pages/APIAccess/APIAccess.test.tsx create mode 100644 services/app-web/src/pages/APIAccess/APIAccess.tsx create mode 100644 services/app-web/src/testHelpers/apolloMocks/apiKeyGQLMocks.ts create mode 100644 services/cypress/integration/thirdPartyAPIAccess/thirdPartyAPIAccess.spec.ts diff --git a/services/app-web/src/constants/routes.ts b/services/app-web/src/constants/routes.ts index 747b7b8875..890ca59c97 100644 --- a/services/app-web/src/constants/routes.ts +++ b/services/app-web/src/constants/routes.ts @@ -9,6 +9,7 @@ const ROUTES = [ 'DASHBOARD_SUBMISSIONS', 'DASHBOARD_RATES', 'GRAPHQL_EXPLORER', + 'API_ACCESS', 'HELP', 'SETTINGS', 'RATES_SUMMARY', @@ -45,6 +46,7 @@ const RoutesRecord: Record = { DASHBOARD_SUBMISSIONS: '/dashboard/submissions', DASHBOARD_RATES: '/dashboard/rate-reviews', GRAPHQL_EXPLORER: '/dev/graphql-explorer', + API_ACCESS: '/dev/api-access', HELP: '/help', SETTINGS: '/settings', RATES_SUMMARY: '/rates/:id', @@ -118,6 +120,7 @@ const PageTitlesRecord: Record = { ROOT: 'Home', AUTH: 'Login', GRAPHQL_EXPLORER: 'GraphQL explorer', + API_ACCESS: 'API Access', HELP: 'Help', SETTINGS: 'Settings', DASHBOARD: 'Dashboard', diff --git a/services/app-web/src/constants/tealium.ts b/services/app-web/src/constants/tealium.ts index 030a7659ff..4aaf4ad0b1 100644 --- a/services/app-web/src/constants/tealium.ts +++ b/services/app-web/src/constants/tealium.ts @@ -36,6 +36,7 @@ const CONTENT_TYPE_BY_ROUTE: Record = { DASHBOARD_RATES: 'table', HELP: 'glossary', GRAPHQL_EXPLORER: 'dev', + API_ACCESS: 'dev', SETTINGS: 'table', RATES_SUMMARY: 'summary', RATE_EDIT: 'form', diff --git a/services/app-web/src/gqlHelpers/apolloErrors.ts b/services/app-web/src/gqlHelpers/apolloErrors.ts index 7e6a6db282..eb1fc3a65f 100644 --- a/services/app-web/src/gqlHelpers/apolloErrors.ts +++ b/services/app-web/src/gqlHelpers/apolloErrors.ts @@ -37,7 +37,7 @@ const handleNetworkError = ( } const handleGQLErrors = (graphQLErrors: GraphQLErrors) => { - graphQLErrors.forEach(({ message, locations, path, extensions }) => { + graphQLErrors.forEach(({ message, locations, path }) => { recordJSException( // Graphql errors mean something is wrong inside our api, maybe bad request or errors we return from api for known edge cases `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` diff --git a/services/app-web/src/index.tsx b/services/app-web/src/index.tsx index c78bd155dc..33c55cd844 100644 --- a/services/app-web/src/index.tsx +++ b/services/app-web/src/index.tsx @@ -16,6 +16,11 @@ import type { S3BucketConfigType } from './s3/s3Amplify' const gqlSchema = loader('../../app-web/src/gen/schema.graphql') +const apiURL = process.env.REACT_APP_API_URL +if (!apiURL || apiURL === '') { + throw new Error('REACT_APP_API_URL must be set to the url for the API') +} + // We are using Amplify for communicating with Cognito, for now. Amplify.configure({ Auth: { @@ -44,7 +49,7 @@ Amplify.configure({ endpoints: [ { name: 'api', - endpoint: process.env.REACT_APP_API_URL, + endpoint: apiURL, }, ], }, diff --git a/services/app-web/src/pages/APIAccess/APIAccess.module.scss b/services/app-web/src/pages/APIAccess/APIAccess.module.scss new file mode 100644 index 0000000000..14a32be4ea --- /dev/null +++ b/services/app-web/src/pages/APIAccess/APIAccess.module.scss @@ -0,0 +1,22 @@ +@use '../../styles/custom.scss' as custom; + +.pageContainer { + padding: 1em 5em; + max-width: custom.$mcr-container-standard-width-fixed; + background-color: custom.$mcr-foundation-white; +} + +.centerButtonContainer { + display: flex; + margin: 1em; + justify-content: center; +} + +.wrapKey { + word-wrap: break-word; + white-space: pre-wrap; + background: custom.$mcr-gray-lighter; + display: block; + padding: 1em; + margin-top: 1em; +} diff --git a/services/app-web/src/pages/APIAccess/APIAccess.test.tsx b/services/app-web/src/pages/APIAccess/APIAccess.test.tsx new file mode 100644 index 0000000000..2d68816800 --- /dev/null +++ b/services/app-web/src/pages/APIAccess/APIAccess.test.tsx @@ -0,0 +1,109 @@ +import { screen } from '@testing-library/react' +import { Route, Routes } from 'react-router-dom' +import { RoutesRecord } from '../../constants' +import { renderWithProviders } from '../../testHelpers' +import { + createAPIKeySuccess, + fetchCurrentUserMock, + mockValidCMSUser, + createAPIKeyNetworkError, +} from '../../testHelpers/apolloMocks' +import { APIAccess } from './APIAccess' + +describe('APIAccess', () => { + afterEach(() => { + jest.resetAllMocks() + }) + + it('renders without errors', async () => { + renderWithProviders( + + } /> + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + ], + }, + routerProvider: { + route: '/dev/api-access', + }, + } + ) + + const instructions = await screen.findByText( + 'To interact with the MC-Review API you will need a valid JWT' + ) + expect(instructions).toBeInTheDocument() + }) + + it('displays an API key on success', async () => { + const { user } = renderWithProviders( + + } /> + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + createAPIKeySuccess(), + ], + }, + routerProvider: { + route: '/dev/api-access', + }, + } + ) + + const generateButton = await screen.findByRole('button', { + name: 'Generate API Key', + }) + await user.click(generateButton) + + const apiKey = await screen.findByRole('code', { name: 'API Key Text' }) + const curlCmd = await screen.findByRole('code', { + name: 'Example Curl Command', + }) + + expect(apiKey).toBeInTheDocument() + expect(apiKey.textContent).toBe('foo.bar.baz.key123') + expect(curlCmd).toBeInTheDocument() + }) + + it('displays error on error', async () => { + const { user } = renderWithProviders( + + } /> + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + createAPIKeyNetworkError(), + ], + }, + routerProvider: { + route: '/dev/api-access', + }, + } + ) + + const generateButton = await screen.findByRole('button', { + name: 'Generate API Key', + }) + await user.click(generateButton) + + const errorMsg = await screen.findByText('System error') + expect(errorMsg).toBeInTheDocument() + }) +}) diff --git a/services/app-web/src/pages/APIAccess/APIAccess.tsx b/services/app-web/src/pages/APIAccess/APIAccess.tsx new file mode 100644 index 0000000000..da8dab7849 --- /dev/null +++ b/services/app-web/src/pages/APIAccess/APIAccess.tsx @@ -0,0 +1,155 @@ +import { ApolloError } from '@apollo/client' +import { Button, Grid, GridContainer, Link } from '@trussworks/react-uswds' +import path from 'path-browserify' +import { useState } from 'react' +import { RoutesRecord } from '../../constants' +import { + CreateApiKeyPayload, + useCreateApiKeyMutation, +} from '../../gen/gqlClient' +import { handleApolloError } from '../../gqlHelpers/apolloErrors' +import { recordJSException } from '../../otelHelpers' +import { GenericErrorPage } from '../Errors/GenericErrorPage' +import styles from './APIAccess.module.scss' + +function APIAccess(): React.ReactElement { + const apiURL = process.env.REACT_APP_API_URL + + const thirdPartyAPIURL = !apiURL + ? undefined + : path.join(apiURL, '/v1/graphql/external') + + const [getAPIKey] = useCreateApiKeyMutation() + + const [displayErrorPage, setDisplayErrorPage] = useState(false) + const [apiKey, setAPIKey] = useState( + undefined + ) + + const callAPIKeyMutation = async () => { + try { + const result = await getAPIKey() + + setAPIKey(result.data?.createAPIKey) + } catch (err) { + console.error('unexpected error generating a new API Key', err) + if (err instanceof ApolloError) { + handleApolloError(err, true) + } + recordJSException(err) + setDisplayErrorPage(true) + } + } + + if (displayErrorPage) { + return + } + + const copyKeyToClipboard = async () => { + if (apiKey) { + await navigator.clipboard.writeText(apiKey.key) + } + } + + const curlCommand = !apiKey + ? undefined + : ` +curl -s ${thirdPartyAPIURL} -X POST \\ +-H "Authorization: Bearer ${apiKey.key}" \\ +-H "Content-Type: application/json" \\ +--data '{"query":"query IndexRates { indexRates { totalCount edges { node { id } } } }"}' +` + return ( + + +

API Access

+
+

Credentials for using the MC-Review API

+
+ To interact with the MC-Review API you will need a valid JWT +
+ + {!apiKey ? ( +
+ +
+ ) : ( + <> + + {apiKey.key} + +
+ + +
+ + )} + +

Usage

+
    +
  • Make GraphQL requests to the URL: {apiURL}
  • +
  • + Include the JWT as a Bearer token in the Authorization + header. Example: +
      +
    • + + Authorization: Bearer eyJhbGciOiJIU... + +
    • +
    +
  • +
+ + {curlCommand && ( + <> + Example curl command: + + {curlCommand} + + + )} + +

Resources

+
    +
  • + The MC-Review  + + GraphQL Explorer + +   will allow you to format and run queries against + the API in all environments except production +
  • +
  • + The  + + official documentation for GraphQL + +
  • +
  • + The MC-Review  + + GraphQL Schema + +   for understanding the shape of data returned by + the API +
  • +
+
+
+ ) +} + +export { APIAccess } diff --git a/services/app-web/src/pages/App/AppRoutes.tsx b/services/app-web/src/pages/App/AppRoutes.tsx index 58b1a9053f..800bd24810 100644 --- a/services/app-web/src/pages/App/AppRoutes.tsx +++ b/services/app-web/src/pages/App/AppRoutes.tsx @@ -39,6 +39,7 @@ import { import { GraphQLExplorer } from '../GraphQLExplorer/GraphQLExplorer' import { RateSummary } from '../SubmissionSummary/RateSummary' import { RateEdit } from '../RateEdit/RateEdit' +import { APIAccess } from '../APIAccess/APIAccess' function componentForAuthMode( authMode: AuthModeType @@ -154,6 +155,7 @@ const StateUserRoutes = ({ element={} /> )} + } /> } /> @@ -239,6 +241,7 @@ const CMSUserRoutes = ({ /> )} } /> + } /> {UniversalRoutes} } /> diff --git a/services/app-web/src/pages/RateEdit/RateEdit.test.tsx b/services/app-web/src/pages/RateEdit/RateEdit.test.tsx index 420d69c305..80b1f4adf1 100644 --- a/services/app-web/src/pages/RateEdit/RateEdit.test.tsx +++ b/services/app-web/src/pages/RateEdit/RateEdit.test.tsx @@ -1,48 +1,51 @@ import { screen, waitFor } from '@testing-library/react' -import { renderWithProviders } from "../../testHelpers" -import { RateEdit } from "./RateEdit" -import { fetchCurrentUserMock, fetchRateMockSuccess, mockValidStateUser } from "../../testHelpers/apolloMocks" +import { renderWithProviders } from '../../testHelpers' +import { RateEdit } from './RateEdit' +import { + fetchCurrentUserMock, + fetchRateMockSuccess, + mockValidStateUser, +} from '../../testHelpers/apolloMocks' import { RoutesRecord } from '../../constants' import { Route, Routes } from 'react-router-dom' // Wrap test component in some top level routes to allow getParams to be tested const wrapInRoutes = (children: React.ReactNode) => { - return ( - - - - ) + return ( + + + + ) } describe('RateEdit', () => { - afterAll(() => jest.clearAllMocks()) + afterAll(() => jest.clearAllMocks()) - describe('Viewing RateEdit as a state user', () => { + describe('Viewing RateEdit as a state user', () => { + it('renders without errors', async () => { + renderWithProviders(wrapInRoutes(), { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidStateUser(), + statusCode: 200, + }), + fetchRateMockSuccess({ + rate: { id: '1337', status: 'UNLOCKED' }, + }), + ], + }, + routerProvider: { + route: '/rates/1337/edit', + }, + featureFlags: { + 'rate-edit-unlock': true, + }, + }) - it('renders without errors', async () => { - renderWithProviders(wrapInRoutes(), { - apolloProvider: { - mocks: [ - fetchCurrentUserMock({ - user: mockValidStateUser(), - statusCode: 200, - }), - fetchRateMockSuccess({ rate: { id: '1337', status: 'UNLOCKED' } }), - ], - }, - routerProvider: { - route: '/rates/1337/edit' - }, - featureFlags: { - 'rate-edit-unlock': true - } - }) - - await waitFor(() => { - expect(screen.queryByTestId('rate-edit')).toBeInTheDocument() - }) + await waitFor(() => { + expect(screen.queryByTestId('rate-edit')).toBeInTheDocument() + }) + }) }) - }) }) - - diff --git a/services/app-web/src/pages/RateEdit/RateEdit.tsx b/services/app-web/src/pages/RateEdit/RateEdit.tsx index af04cbfb00..2225f2c412 100644 --- a/services/app-web/src/pages/RateEdit/RateEdit.tsx +++ b/services/app-web/src/pages/RateEdit/RateEdit.tsx @@ -1,52 +1,54 @@ -import React from "react"; -import { useNavigate, useParams } from "react-router-dom"; -import { useFetchRateQuery } from "../../gen/gqlClient"; -import { GridContainer } from "@trussworks/react-uswds"; -import { Loading } from "../../components"; -import { GenericErrorPage } from "../Errors/GenericErrorPage"; +import React from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { useFetchRateQuery } from '../../gen/gqlClient' +import { GridContainer } from '@trussworks/react-uswds' +import { Loading } from '../../components' +import { GenericErrorPage } from '../Errors/GenericErrorPage' type RouteParams = { - id: string + id: string } export const RateEdit = (): React.ReactElement => { - const navigate = useNavigate() - const { id } = useParams() - if (!id) { - throw new Error( - 'PROGRAMMING ERROR: id param not set in state submission form.' - ) - } + const navigate = useNavigate() + const { id } = useParams() + if (!id) { + throw new Error( + 'PROGRAMMING ERROR: id param not set in state submission form.' + ) + } - const { data, loading, error } = useFetchRateQuery({ - variables: { - input: { - rateID: id, + const { data, loading, error } = useFetchRateQuery({ + variables: { + input: { + rateID: id, + }, }, - }, - }) + }) - const rate = data?.fetchRate.rate + const rate = data?.fetchRate.rate - if (loading) { - return ( - - - - ) - } else if (error || !rate ) { - return - } + if (loading) { + return ( + + + + ) + } else if (error || !rate) { + return + } - if (rate.status !== 'UNLOCKED') { - navigate(`/rates/${id}`) - } + if (rate.status !== 'UNLOCKED') { + navigate(`/rates/${id}`) + } - return ( -

- You've reached the '/rates/:id/edit' url placeholder for the incoming standalone edit rate form -
- Ticket: MCR-3771 -

- ) -} \ No newline at end of file + return ( +

+ You've reached the '/rates/:id/edit' url placeholder for the + incoming standalone edit rate form +
+ Ticket:{' '} + MCR-3771 +

+ ) +} diff --git a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx index 604454012b..dc39c48d6b 100644 --- a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx +++ b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Link, SideNav, GridContainer, Icon } from '@trussworks/react-uswds' import { NavLink } from 'react-router-dom' import styles from './SubmissionSideNav.module.scss' diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx index 490f69e143..ca00baba01 100644 --- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx @@ -146,15 +146,15 @@ describe('RateSummary', () => { it('redirects to RateEdit component from RateSummary without errors for unlocked rate', async () => { renderWithProviders( - } + } /> - } + } /> - , + , { apolloProvider: { mocks: [ @@ -162,16 +162,19 @@ describe('RateSummary', () => { user: mockValidStateUser(), statusCode: 200, }), - fetchRateMockSuccess({ rate: { id: '1337', status: 'UNLOCKED' } }), + fetchRateMockSuccess({ + rate: { id: '1337', status: 'UNLOCKED' }, + }), ], }, routerProvider: { - route: '/rates/1337' + route: '/rates/1337', }, - featureFlags: { - 'rate-edit-unlock': true - } - }) + featureFlags: { + 'rate-edit-unlock': true, + }, + } + ) await waitFor(() => { expect(screen.queryByTestId('rate-edit')).toBeInTheDocument() diff --git a/services/app-web/src/testHelpers/apolloMocks/apiKeyGQLMocks.ts b/services/app-web/src/testHelpers/apolloMocks/apiKeyGQLMocks.ts new file mode 100644 index 0000000000..de323df96b --- /dev/null +++ b/services/app-web/src/testHelpers/apolloMocks/apiKeyGQLMocks.ts @@ -0,0 +1,33 @@ +import { MockedResponse } from "@apollo/client/testing" +import { CreateApiKeyDocument, CreateApiKeyMutation } from "../../gen/gqlClient" + +function createAPIKeySuccess(): MockedResponse { + + return { + request: { + query: CreateApiKeyDocument, + }, + result: { + data: { + createAPIKey: { + key: 'foo.bar.baz.key123', + expiresAt: '2025-01-31T21:08:57.951Z', + }, + }, + }, + } +} + +function createAPIKeyNetworkError(): MockedResponse { + return { + request: { + query: CreateApiKeyDocument, + }, + error: new Error('A network error occurred'), + } +} + +export { + createAPIKeySuccess, + createAPIKeyNetworkError, +} diff --git a/services/app-web/src/testHelpers/apolloMocks/index.ts b/services/app-web/src/testHelpers/apolloMocks/index.ts index f71b424cc5..400a7113a7 100644 --- a/services/app-web/src/testHelpers/apolloMocks/index.ts +++ b/services/app-web/src/testHelpers/apolloMocks/index.ts @@ -57,3 +57,8 @@ export { indexRatesMockSuccess, indexRatesMockFailure } from './rateGQLMocks' export { updateUserMockError, updateUserMockSuccess } from './updateUserMock' export { fetchRateMockSuccess } from './rateGQLMocks' + +export { + createAPIKeySuccess, + createAPIKeyNetworkError, +} from './apiKeyGQLMocks' diff --git a/services/cypress/integration/thirdPartyAPIAccess/thirdPartyAPIAccess.spec.ts b/services/cypress/integration/thirdPartyAPIAccess/thirdPartyAPIAccess.spec.ts new file mode 100644 index 0000000000..f6befbecee --- /dev/null +++ b/services/cypress/integration/thirdPartyAPIAccess/thirdPartyAPIAccess.spec.ts @@ -0,0 +1,78 @@ +describe('thirdPartyAPIAccess', () => { + + beforeEach(() => { + cy.stubFeatureFlags() + cy.interceptGraphQL() + }) + + + it('gets an error back without authentication', () => { + + // get the API URL! + const url = Cypress.env('API_URL') + const api_url = url + '/v1/graphql/external' + + cy.request({ + url: api_url, + headers: { + 'Authorization': 'Bearer foobar' + }, + failOnStatusCode: false, + }).then(res => { + expect(res.status).to.equal(403) // unauthenticated?? + }) + + }) + + it('gets an error back without no auth header sent', () => { + + // get the API URL! + const url = Cypress.env('API_URL') + const api_url = url + '/v1/graphql/external' + + cy.request({ + url: api_url, + failOnStatusCode: false, + }).then(res => { + expect(res.status).to.equal(401) // unauthenticated + }) + }) + + it('works with a valid key', () => { + + // get the API URL! + const url = Cypress.env('API_URL') + const api_url = url + '/v1/graphql/external' + + // sign in and get to the api key url + cy.logInAsCMSUser() + + cy.visit('/dev/api-access') + + cy.findByRole('button', { + name: "Generate API Key", + }).click() + + + cy.get("[aria-label='API Key Text']").then((codeBlock) => { + + const apiKey = codeBlock.text().trim() + const bearer = `Bearer ${apiKey}` + + cy.request({ + method: 'post', + url: api_url, + headers: { + 'Content-Type': 'application/json', + Authorization: bearer, + }, + body: '{"query":"query IndexRates { indexRates { totalCount edges { node { id } } } }"}', + failOnStatusCode: false, + }).then(res => { + expect(res.status).to.equal(200) + }) + + }) + }) + +}) From 3b5c86896d86b4ef0c4b6aab07a0ef0fcf22de41 Mon Sep 17 00:00:00 2001 From: haworku Date: Thu, 1 Feb 2024 14:37:14 -0800 Subject: [PATCH 09/24] Update existing tech design doc to include MM discovery doc language (#2221) --- .../technical-design-discussion-template.md | 71 ++++++------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/docs/templates/technical-design-discussion-template.md b/docs/templates/technical-design-discussion-template.md index 1e765e26e4..2fe0d8c1a7 100644 --- a/docs/templates/technical-design-discussion-template.md +++ b/docs/templates/technical-design-discussion-template.md @@ -1,79 +1,54 @@ --- -title: Technical Design Discussion Template +title: Technical Discovery Template --- -## Title of Design Doc +# Title of Design Discovery Doc ## Overview -In one or two sentences, -summarize what the problem is and how you intend on solving it. -It's helpful to outline the stakeholders here, -as it informs why this problem merits solving. +In one or two sentences, summarize the problem (may be a feature epic under consideration) + +## Links + +Jira ticket links or diagrams ## Constraints (optional) -Write what this document will not cover. +Write what this document will not cover ## Terminology (optional) -Define any terminology you are using that could be application system jargon +Define any application system jargon -## Background +## Discovery This section is for going into more detail about the problem. -- Who are the stakeholders and how have they been impacted? -- Historically, what effect has the problem it had? -- Is there data to illustrate the impact? -- Is there an existing solution? - If so, why does it need to be improved on? -- Are there past projects or designs to link for context? +### What do we need to be able to do? -## Examples +### How should we do it? -At least one example should be used to help the audience understand the context better. +### What relevant code or tooling do we have already? -## Proposed Solution +### What architectural considerations exist? +examples: new or adjusted programming patterns, new or adjusted infrastructure, new libraries introduced, permissions modeling changes, data model changes. Was there a build vs. buy solution? Was there scope considerations or tradeoff? -Detail your solution here. -Start with a broad overview and then go into detail on each portion of the design. +### How will we test this ? -- What changes will the stakeholder/client see? - This should clearly illustrate how the stakeholders' needs are being met -- How exactly does it solve the problem outlined above? - Explain how this solution applies to the use cases identified. -- Why are you picking this solution? -- What are the limits of the proposed solution? - At what point will the design cease to be a viable solution? -- How will you measure the success of your solution? -- If priorities shift or the solution becomes too cumbersome to implement, how will you roll back? -Visual representations of the solution can be helpful here (ex. a diagram for a request lifecycle). - -## Implementation +## Proposed Solution -Without going too much into individual tasks, -write an overview of what this solution's implementation would look like. +Detail your solution here. Start with a broad overview and then list the ticket-level tasks as you see them. -- Can this be broken down into multiple technical efforts? -- What is the tech stack involved? -- Will additional infrastructure be needed to achieve this? -- How will you test this? +Visual representations of the solution can be helpful here as well. -## Trade-offs +## Dependencies (optional) -- This section should go over other possible solutions, - and why you chose yours over them. -- Was there a build vs. buy solution? -- What industry standards/practices already exist? -- Why is your solution better? +Are there any dependencies on other feature epics or third party tools? Do we need anything from other teams? -## Cross Team Dependencies (optional) +## Open Questions -If there are multiple teams or clients that are dependencies for implementation to be successful, -list them here. -Examples of this are security or design collaboration/reviews. +Add any open questions still remaining. ## Further Reading From 15d1f6340a5c18dc038f4c419a7fef7aedcbb586 Mon Sep 17 00:00:00 2001 From: haworku Date: Thu, 1 Feb 2024 16:21:54 -0800 Subject: [PATCH 10/24] Send utag.view tag data on initial app load (#2215) * Call utag.view also on initial app load * Cleanup --- services/app-web/src/hooks/useTealium.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/services/app-web/src/hooks/useTealium.ts b/services/app-web/src/hooks/useTealium.ts index c6bf8cec59..e0e833bcb3 100644 --- a/services/app-web/src/hooks/useTealium.ts +++ b/services/app-web/src/hooks/useTealium.ts @@ -90,10 +90,25 @@ const useTealium = (): { document.body.appendChild(loadTagsSnippet) + const tagData: TealiumViewDataObject = { + content_language: 'en', + content_type: `${CONTENT_TYPE_BY_ROUTE[currentRoute]}`, + page_name: tealiumPageName, + page_path: pathname, + site_domain: 'cms.gov', + site_environment: `${process.env.REACT_APP_STAGE_NAME}`, + site_section: `${currentRoute}`, + logged_in: `${Boolean(loggedInUser) ?? false}`, + } + window.utag.view(tagData) + return () => { // document.body.removeChild(loadTagsSnippet) document.head.removeChild(initializeTagManagerSnippet) } + + // NOTE: Run effect once on component mount, we recheck dependencies if effect is updated in the subsequent page view effect + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // Add page view From 0107a0275f40025a86f0fc96600bd894be54ea71 Mon Sep 17 00:00:00 2001 From: haworku Date: Fri, 2 Feb 2024 09:32:07 -0800 Subject: [PATCH 11/24] bugfix: make sure utag.view exists (#2230) * make sure utag.view exists * fix build error --- services/app-web/src/hooks/useTealium.ts | 28 +++++++++--------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/services/app-web/src/hooks/useTealium.ts b/services/app-web/src/hooks/useTealium.ts index e0e833bcb3..7680a9dad2 100644 --- a/services/app-web/src/hooks/useTealium.ts +++ b/services/app-web/src/hooks/useTealium.ts @@ -90,39 +90,31 @@ const useTealium = (): { document.body.appendChild(loadTagsSnippet) - const tagData: TealiumViewDataObject = { - content_language: 'en', - content_type: `${CONTENT_TYPE_BY_ROUTE[currentRoute]}`, - page_name: tealiumPageName, - page_path: pathname, - site_domain: 'cms.gov', - site_environment: `${process.env.REACT_APP_STAGE_NAME}`, - site_section: `${currentRoute}`, - logged_in: `${Boolean(loggedInUser) ?? false}`, - } - window.utag.view(tagData) - return () => { // document.body.removeChild(loadTagsSnippet) document.head.removeChild(initializeTagManagerSnippet) } - - // NOTE: Run effect once on component mount, we recheck dependencies if effect is updated in the subsequent page view effect - // eslint-disable-next-line react-hooks/exhaustive-deps }, []) // Add page view // this effect should fire on each page view or if something changes about logged in user useEffect(() => { + // Do not add tealium for local dev or review apps if (process.env.REACT_APP_AUTH_MODE !== 'IDM') { - // console.info(`mock tealium page view: ${tealiumPageName}`) return } - // Guardrail - protect against trying to call utag before its loaded. + const waitForUtag = async () => { + return new Promise(resolve => setTimeout(resolve, 1000)); + } + + if (!window.utag) { - return + waitForUtag().catch(() => { /* All of this is a guardrail - protect against trying to call utag before its loaded*/ }) + if (!window.utag) { + return + } } // eslint-disable-next-line @typescript-eslint/no-empty-function From 47ffc55b4db3fcbf810fda64ceff858da944e5d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:15:23 -0500 Subject: [PATCH 12/24] Bump @aws-sdk/client-amplify from 3.485.0 to 3.504.0 (#2226) Bumps [@aws-sdk/client-amplify](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-amplify) from 3.485.0 to 3.504.0. - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-amplify/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.504.0/clients/client-amplify) --- updated-dependencies: - dependency-name: "@aws-sdk/client-amplify" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 433 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 388 insertions(+), 45 deletions(-) diff --git a/yarn.lock b/yarn.lock index 42a34ba21b..c090e60139 100644 --- a/yarn.lock +++ b/yarn.lock @@ -748,49 +748,49 @@ tslib "^1.8.0" "@aws-sdk/client-amplify@^3.485.0": - version "3.485.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-amplify/-/client-amplify-3.485.0.tgz#816f6c545c40660d71ec7fdabeaa9041cdf1f77a" - integrity sha512-HAslDyi5yNKl7VUF5DwCHa9rWAPVza/U45XP/oT6BAd16IOoMmDzTIGhG68Jc8+gzqAd1qkTai2Qq17KYtwJhw== + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-amplify/-/client-amplify-3.504.0.tgz#940ce3bb5d0cdb280ca224d838ac7cd3aa6171a5" + integrity sha512-YcYjmbEOGveGCPffv2zrQbDuapZwxnLYotjKn1dEaT9iGTuDz8uFujNfx4VRPNr/qqa6mgtn0fH+Px+4YmrLag== dependencies: "@aws-crypto/sha256-browser" "3.0.0" "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/client-sts" "3.485.0" - "@aws-sdk/core" "3.485.0" - "@aws-sdk/credential-provider-node" "3.485.0" - "@aws-sdk/middleware-host-header" "3.485.0" - "@aws-sdk/middleware-logger" "3.485.0" - "@aws-sdk/middleware-recursion-detection" "3.485.0" - "@aws-sdk/middleware-signing" "3.485.0" - "@aws-sdk/middleware-user-agent" "3.485.0" - "@aws-sdk/region-config-resolver" "3.485.0" - "@aws-sdk/types" "3.485.0" - "@aws-sdk/util-endpoints" "3.485.0" - "@aws-sdk/util-user-agent-browser" "3.485.0" - "@aws-sdk/util-user-agent-node" "3.485.0" - "@smithy/config-resolver" "^2.0.23" - "@smithy/core" "^1.2.2" - "@smithy/fetch-http-handler" "^2.3.2" - "@smithy/hash-node" "^2.0.18" - "@smithy/invalid-dependency" "^2.0.16" - "@smithy/middleware-content-length" "^2.0.18" - "@smithy/middleware-endpoint" "^2.3.0" - "@smithy/middleware-retry" "^2.0.26" - "@smithy/middleware-serde" "^2.0.16" - "@smithy/middleware-stack" "^2.0.10" - "@smithy/node-config-provider" "^2.1.9" - "@smithy/node-http-handler" "^2.2.2" - "@smithy/protocol-http" "^3.0.12" - "@smithy/smithy-client" "^2.2.1" - "@smithy/types" "^2.8.0" - "@smithy/url-parser" "^2.0.16" - "@smithy/util-base64" "^2.0.1" - "@smithy/util-body-length-browser" "^2.0.1" - "@smithy/util-body-length-node" "^2.1.0" - "@smithy/util-defaults-mode-browser" "^2.0.24" - "@smithy/util-defaults-mode-node" "^2.0.32" - "@smithy/util-endpoints" "^1.0.8" - "@smithy/util-retry" "^2.0.9" - "@smithy/util-utf8" "^2.0.2" + "@aws-sdk/client-sts" "3.504.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/credential-provider-node" "3.504.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-signing" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" tslib "^2.5.0" "@aws-sdk/client-cloudformation@^3.410.0", "@aws-sdk/client-cloudformation@^3.485.0": @@ -1685,6 +1685,51 @@ tslib "^2.5.0" uuid "^8.3.2" +"@aws-sdk/client-sso-oidc@3.504.0": + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.504.0.tgz#78ed1e921fcda039e8525a92bf5760e8bd0091a8" + integrity sha512-ODA33/nm2srhV08EW0KZAP577UgV0qjyr7Xp2yEo8MXWL4ZqQZprk1c+QKBhjr4Djesrm0VPmSD/np0mtYP68A== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sts" "3.504.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-signing" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + "@aws-sdk/client-sso@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.186.0.tgz#233bdd1312dbf88ef9452f8a62c3c3f1ac580330" @@ -1851,6 +1896,49 @@ "@smithy/util-utf8" "^2.1.1" tslib "^2.5.0" +"@aws-sdk/client-sso@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.502.0.tgz#8cf21d8f52a5bef65bf7b458051b3f61f7db822c" + integrity sha512-OZAYal1+PQgUUtWiHhRayDtX0OD+XpXHKAhjYgEIPbyhQaCMp3/Bq1xDX151piWXvXqXLJHFKb8DUEqzwGO9QA== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + "@aws-sdk/client-sts@3.186.3": version "3.186.3" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.186.3.tgz#1c12355cb9d3cadc64ab74c91c3d57515680dfbd" @@ -2031,7 +2119,7 @@ fast-xml-parser "4.2.5" tslib "^2.5.0" -"@aws-sdk/client-sts@3.499.0", "@aws-sdk/client-sts@^3.410.0": +"@aws-sdk/client-sts@3.499.0": version "3.499.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.499.0.tgz#4c8260ed1fda7ad2c4e5fe12e4eaa5849da77d92" integrity sha512-Eyj9STw2DXMtXL5V/v0HYHO6+JjGPi257M5IYyxwqlvRchq6jbOsedobfxclB/gBUyBRtZdnyAIS8uCKjb4kpA== @@ -2077,6 +2165,51 @@ fast-xml-parser "4.2.5" tslib "^2.5.0" +"@aws-sdk/client-sts@3.504.0", "@aws-sdk/client-sts@^3.410.0": + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.504.0.tgz#78a0beaf988ad2647d79c7157083dfd55953f41e" + integrity sha512-IESs8FkL7B/uY+ml4wgoRkrr6xYo4PizcNw6JX17eveq1gRBCPKeGMjE6HTDOcIYZZ8rqz/UeuH3JD4UhrMOnA== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-middleware" "^2.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + fast-xml-parser "4.2.5" + tslib "^2.5.0" + "@aws-sdk/client-textract@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/client-textract/-/client-textract-3.6.1.tgz#b8972f53f0353222b4c052adc784291e602be6aa" @@ -2247,6 +2380,16 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-env@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.502.0.tgz#800e63b2b9d90b078a120d474d5a3b1ec5b48514" + integrity sha512-KIB8Ae1Z7domMU/jU4KiIgK4tmYgvuXlhR54ehwlVHxnEoFPoPuGHFZU7oFn79jhhSLUFQ1lRYMxP0cEwb7XeQ== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-env@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.6.1.tgz#d8b2dd36836432a9b8ec05a5cf9fe428b04c9964" @@ -2256,6 +2399,21 @@ "@aws-sdk/types" "3.6.1" tslib "^1.8.0" +"@aws-sdk/credential-provider-http@3.503.1": + version "3.503.1" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.503.1.tgz#e882a4b740c9193650053033b3001b03ca4b12c8" + integrity sha512-rTdlFFGoPPFMF2YjtlfRuSgKI+XsF49u7d98255hySwhsbwd3Xp+utTTPquxP+CwDxMHbDlI7NxDzFiFdsoZug== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/util-stream" "^2.1.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-imds@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.186.0.tgz#73e0f62832726c7734b4f6c50a02ab0d869c00e1" @@ -2338,6 +2496,23 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-ini@3.504.0": + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.504.0.tgz#d463dae3a309c1e9181811f27c484fd9bfa6821f" + integrity sha512-ODICLXfr8xTUd3wweprH32Ge41yuBa+u3j0JUcLdTUO1N9ldczSMdo8zOPlP0z4doqD3xbnqMkjNQWgN/Q+5oQ== + dependencies: + "@aws-sdk/client-sts" "3.504.0" + "@aws-sdk/credential-provider-env" "3.502.0" + "@aws-sdk/credential-provider-process" "3.502.0" + "@aws-sdk/credential-provider-sso" "3.504.0" + "@aws-sdk/credential-provider-web-identity" "3.504.0" + "@aws-sdk/types" "3.502.0" + "@smithy/credential-provider-imds" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-ini@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.6.1.tgz#0da6d9341e621f8e0815814ed017b88e268fbc3d" @@ -2432,6 +2607,24 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-node@3.504.0": + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.504.0.tgz#c6014f56dd59be295114290164e00375d33f2952" + integrity sha512-6+V5hIh+tILmUjf2ZQWQINR3atxQVgH/bFrGdSR/sHSp/tEgw3m0xWL3IRslWU1e4/GtXrfg1iYnMknXy68Ikw== + dependencies: + "@aws-sdk/credential-provider-env" "3.502.0" + "@aws-sdk/credential-provider-http" "3.503.1" + "@aws-sdk/credential-provider-ini" "3.504.0" + "@aws-sdk/credential-provider-process" "3.502.0" + "@aws-sdk/credential-provider-sso" "3.504.0" + "@aws-sdk/credential-provider-web-identity" "3.504.0" + "@aws-sdk/types" "3.502.0" + "@smithy/credential-provider-imds" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-node@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.6.1.tgz#0055292a4f0f49d053e8dfcc9174d8d2cf6862bb" @@ -2489,6 +2682,17 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-process@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.502.0.tgz#6c41d8845a1c7073491a064c158363de04640381" + integrity sha512-fJJowOjQ4infYQX0E1J3xFVlmuwEYJAFk0Mo1qwafWmEthsBJs+6BR2RiWDELHKrSK35u4Pf3fu3RkYuCtmQFw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-process@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.6.1.tgz#5bf851f3ee232c565b8c82608926df0ad28c1958" @@ -2550,6 +2754,19 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-sso@3.504.0": + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.504.0.tgz#ad036805e8677f6a53b24aa82991596aa11ac605" + integrity sha512-4MgH2or2SjPzaxM08DCW+BjaX4DSsEGJlicHKmz6fh+w9JmLh750oXcTnbvgUeVz075jcs6qTKjvUcsdGM/t8Q== + dependencies: + "@aws-sdk/client-sso" "3.502.0" + "@aws-sdk/token-providers" "3.504.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-web-identity@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.186.0.tgz#db43f37f7827b553490dd865dbaa9a2c45f95494" @@ -2589,6 +2806,17 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-web-identity@3.504.0": + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.504.0.tgz#53de7dee538ecaeec534e369bca76c546b8f4cc5" + integrity sha512-L1ljCvGpIEFdJk087ijf2ohg7HBclOeB1UgBxUBBzf4iPRZTQzd2chGaKj0hm2VVaXz7nglswJeURH5PFcS5oA== + dependencies: + "@aws-sdk/client-sts" "3.504.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/eventstream-codec@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/eventstream-codec/-/eventstream-codec-3.186.0.tgz#9da9608866b38179edf72987f2bc3b865d11db13" @@ -2885,6 +3113,16 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/middleware-host-header@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.502.0.tgz#2651fb3509990271c89eb50133fb17cb8ae435f6" + integrity sha512-EjnG0GTYXT/wJBmm5/mTjDcAkzU8L7wQjOzd3FTXuTCNNyvAvwrszbOj5FlarEw5XJBbQiZtBs+I5u9+zy560w== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/middleware-host-header@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.6.1.tgz#6e1b4b95c5bfea5a4416fa32f11d8fa2e6edaeff" @@ -2938,6 +3176,15 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/middleware-logger@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.502.0.tgz#558cefdd233779f15687957f9f07497199b22d72" + integrity sha512-FDyv6K4nCoHxbjLGS2H8ex8I0KDIiu4FJgVRPs140ZJy6gE5Pwxzv6YTzZGLMrnqcIs9gh065Lf6DjwMelZqaw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/middleware-logger@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.6.1.tgz#78b3732cf188d5e4df13488db6418f7f98a77d6d" @@ -2985,6 +3232,16 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/middleware-recursion-detection@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.502.0.tgz#c22e2c0c1d551e58c788264687324bb7186af2cc" + integrity sha512-hvbyGJbxeuezxOu8VfFmcV4ql1hKXLxHTe5FNYfEBat2KaZXVhc1Hg+4TvB06/53p+E8J99Afmumkqbxs2esUA== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/middleware-retry@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.186.0.tgz#0ff9af58d73855863683991a809b40b93c753ad1" @@ -3132,6 +3389,19 @@ "@smithy/util-middleware" "^2.1.1" tslib "^2.5.0" +"@aws-sdk/middleware-signing@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.502.0.tgz#48b3503147eecb1a53a63633462de353668f635a" + integrity sha512-4hF08vSzJ7L6sB+393gOFj3s2N6nLusYS0XrMW6wYNFU10IDdbf8Z3TZ7gysDJJHEGQPmTAesPEDBsasGWcMxg== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/signature-v4" "^2.1.1" + "@smithy/types" "^2.9.1" + "@smithy/util-middleware" "^2.1.1" + tslib "^2.5.0" + "@aws-sdk/middleware-signing@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.6.1.tgz#e70a2f35d85d70e33c9fddfb54b9520f6382db16" @@ -3207,6 +3477,17 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/middleware-user-agent@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.502.0.tgz#dd740f150d6f3110cf5b08fedf361d202f899c93" + integrity sha512-TxbBZbRiXPH0AUxegqiNd9aM9zNSbfjtBs5MEfcBsweeT/B2O7K1EjP9+CkB8Xmk/5FLKhAKLr19b1TNoE27rw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/middleware-user-agent@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.6.1.tgz#6845dfb3bc6187897f348c2c87dec833e6a65c99" @@ -3359,6 +3640,18 @@ "@smithy/util-middleware" "^2.1.1" tslib "^2.5.0" +"@aws-sdk/region-config-resolver@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.502.0.tgz#c18a04060879eb03c47c05b05fc296119ee073ba" + integrity sha512-mxmsX2AGgnSM+Sah7mcQCIneOsJQNiLX0COwEttuf8eO+6cLMAZvVudH3BnWTfea4/A9nuri9DLCqBvEmPrilg== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/types" "^2.9.1" + "@smithy/util-config-provider" "^2.2.1" + "@smithy/util-middleware" "^2.1.1" + tslib "^2.5.0" + "@aws-sdk/s3-request-presigner@^3.485.0": version "3.485.0" resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.485.0.tgz#c0ae7d1e241318de24307f0b84e01cdf5d0c8a97" @@ -3592,6 +3885,18 @@ "@smithy/util-utf8" "^2.1.1" tslib "^2.5.0" +"@aws-sdk/token-providers@3.504.0": + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.504.0.tgz#f7f60b1152458e7094529ea3f3ced6ce92eece9f" + integrity sha512-YIJWWsZi2ClUiILS1uh5L6VjmCUSTI6KKMuL9DkGjYqJ0aI6M8bd8fT9Wm7QmXCyjcArTgr/Atkhia4T7oKvzQ== + dependencies: + "@aws-sdk/client-sso-oidc" "3.504.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/types@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.186.0.tgz#f6fb6997b6a364f399288bfd5cd494bc680ac922" @@ -3613,7 +3918,7 @@ "@smithy/types" "^2.8.0" tslib "^2.5.0" -"@aws-sdk/types@3.496.0", "@aws-sdk/types@^3.1.0", "@aws-sdk/types@^3.222.0": +"@aws-sdk/types@3.496.0": version "3.496.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.496.0.tgz#cdde44a94a57cf8f97cf05e4d0bdce2f56ce4eeb" integrity sha512-umkGadK4QuNQaMoDICMm7NKRI/mYSXiyPjcn3d53BhsuArYU/52CebGQKdt4At7SwwsiVJZw9RNBHyN5Mm0HVw== @@ -3621,6 +3926,14 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/types@3.502.0", "@aws-sdk/types@^3.1.0", "@aws-sdk/types@^3.222.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.502.0.tgz#c23dda4df7fdbe32642d4f5ab23516f455fb6aba" + integrity sha512-M0DSPYe/gXhwD2QHgoukaZv5oDxhW3FfvYIrJptyqUq3OnPJBcDbihHjrE0PBtfh/9kgMZT60/fQ2NVFANfa2g== + dependencies: + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/types@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.6.1.tgz#00686db69e998b521fcd4a5f81ef0960980f80c4" @@ -3800,6 +4113,16 @@ "@smithy/util-endpoints" "^1.1.1" tslib "^2.5.0" +"@aws-sdk/util-endpoints@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.502.0.tgz#aee818c0c53dfedfd49599fc260cd880faea5e82" + integrity sha512-6LKFlJPp2J24r1Kpfoz5ESQn+1v5fEjDB3mtUKRdpwarhm3syu7HbKlHCF3KbcCOyahobvLvhoedT78rJFEeeg== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + "@smithy/util-endpoints" "^1.1.1" + tslib "^2.5.0" + "@aws-sdk/util-format-url@3.485.0": version "3.485.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.485.0.tgz#eab01bf10153aa6cce0c6edfb1e48c5088e5993c" @@ -3901,6 +4224,16 @@ bowser "^2.11.0" tslib "^2.5.0" +"@aws-sdk/util-user-agent-browser@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.502.0.tgz#87b42abff6944052c78a84981637ac21859dd016" + integrity sha512-v8gKyCs2obXoIkLETAeEQ3AM+QmhHhst9xbM1cJtKUGsRlVIak/XyyD+kVE6kmMm1cjfudHpHKABWk9apQcIZQ== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/types" "^2.9.1" + bowser "^2.11.0" + tslib "^2.5.0" + "@aws-sdk/util-user-agent-browser@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.6.1.tgz#11b9cc8743392761adb304460f4b54ec8acc2ee6" @@ -3949,6 +4282,16 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/util-user-agent-node@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.502.0.tgz#04ac4d0371d4f243f12ddc23b42ca8ceb27dfad9" + integrity sha512-9RjxpkGZKbTdl96tIJvAo+vZoz4P/cQh36SBUt9xfRfW0BtsaLyvSrvlR5wyUYhvRcC12Axqh/8JtnAPq//+Vw== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/util-user-agent-node@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.6.1.tgz#98384095fa67d098ae7dd26f3ccaad028e8aebb6" @@ -11873,9 +12216,9 @@ integrity sha512-Abq9fBviLV93OiXMu+f6r0elxCzRwc0RC5f99cU892uBITL44pTvgvEqlRlPRi8EGcO1z7Cp8A4d0s/p3J/+Nw== "@types/node@^16.18.39": - version "16.18.75" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.75.tgz#88460b2706e5be1788f5ed6ef51152283b7703a2" - integrity sha512-+FSfZd5mpMDTcIK7bp2GueIcAespzR4FROOXnEst248c85vwthIEwtXYOLgVc/sI4ihE1K/7yO1lEiSgvwAOxA== + version "16.18.78" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.78.tgz#3d97264128712f2eb59f1f8456bcfc5d56d8105c" + integrity sha512-2poPMDdsGfvhcLmgJZ85QrIfN6z3PijYRMiV0FWIEUiQW/t/lzH7BEm4vN+HMhjZXbtIKssMcAxTcgu4Rm83YA== "@types/normalize-package-data@^2.4.0": version "2.4.1" From 49f5fb19768a5a35d289904c1cd016758257877b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:15:45 -0500 Subject: [PATCH 13/24] Bump @aws-sdk/s3-request-presigner from 3.485.0 to 3.504.0 (#2225) Bumps [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-request-presigner) from 3.485.0 to 3.504.0. - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-request-presigner/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.504.0/packages/s3-request-presigner) --- updated-dependencies: - dependency-name: "@aws-sdk/s3-request-presigner" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 91 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/yarn.lock b/yarn.lock index c090e60139..81ae7cd7c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3293,21 +3293,6 @@ "@smithy/types" "^2.8.0" tslib "^2.5.0" -"@aws-sdk/middleware-sdk-s3@3.485.0": - version "3.485.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.485.0.tgz#89e48e32869afb3568cec40558c9cf04d4b72544" - integrity sha512-3769c4e3UtvaNU5T6dHxhjGI1kEXymldqiP1PMZMX2jVffwSGhbvyLq0Kl6+9Jr51fj2oXN6Tex+8J9+5dzTgQ== - dependencies: - "@aws-sdk/types" "3.485.0" - "@aws-sdk/util-arn-parser" "3.465.0" - "@smithy/node-config-provider" "^2.1.9" - "@smithy/protocol-http" "^3.0.12" - "@smithy/signature-v4" "^2.0.0" - "@smithy/smithy-client" "^2.2.1" - "@smithy/types" "^2.8.0" - "@smithy/util-config-provider" "^2.1.0" - tslib "^2.5.0" - "@aws-sdk/middleware-sdk-s3@3.496.0": version "3.496.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.496.0.tgz#8d15cd44578da34159d99282b5de734a0f50db7c" @@ -3323,6 +3308,21 @@ "@smithy/util-config-provider" "^2.2.1" tslib "^2.5.0" +"@aws-sdk/middleware-sdk-s3@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.502.0.tgz#a2d968414247fd9cbfc90e1071f29e4375cb25b8" + integrity sha512-GbGugrfyL5bNA/zw8iQll92yXBONfWSC8Ns00DtkOU1saPXp4/7WHtyyZGYdvPa73T1IsuZy9egpoYRBmRcd5Q== + dependencies: + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-arn-parser" "3.495.0" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/signature-v4" "^2.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/util-config-provider" "^2.2.1" + tslib "^2.5.0" + "@aws-sdk/middleware-sdk-sts@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.186.0.tgz#18f3d6b7b42c1345b5733ac3e3119d370a403e94" @@ -3653,17 +3653,17 @@ tslib "^2.5.0" "@aws-sdk/s3-request-presigner@^3.485.0": - version "3.485.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.485.0.tgz#c0ae7d1e241318de24307f0b84e01cdf5d0c8a97" - integrity sha512-5TCyl1H/PdBH0XDSILb9y1d/fU+tDEQ7Fkqeb2gIYENDG09dX68TtcZVGs0sMZtC9CLUFpmEp8R/3LtfuoeY6w== + version "3.504.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.504.0.tgz#4288f6381c1a63fae417754eae4f0652cf0a51b5" + integrity sha512-5FxVdRufiFLSUDJ/Qul5JFPHjhFFzo+C6u53bzbi7gaSshA6lLLhJ9KbVk2LmKE1mTR+nh2+JebI6y+3njtkzw== dependencies: - "@aws-sdk/signature-v4-multi-region" "3.485.0" - "@aws-sdk/types" "3.485.0" - "@aws-sdk/util-format-url" "3.485.0" - "@smithy/middleware-endpoint" "^2.3.0" - "@smithy/protocol-http" "^3.0.12" - "@smithy/smithy-client" "^2.2.1" - "@smithy/types" "^2.8.0" + "@aws-sdk/signature-v4-multi-region" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-format-url" "3.502.0" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" tslib "^2.5.0" "@aws-sdk/service-error-classification@3.186.0": @@ -3691,18 +3691,6 @@ dependencies: tslib "^1.8.0" -"@aws-sdk/signature-v4-multi-region@3.485.0": - version "3.485.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.485.0.tgz#1a32f61dc205e9b3b4b590aeccb19743483f3de7" - integrity sha512-168ipXkbG75l9cKQmsBtx/4+AYjGsBoy724bXosW13t2/l/E3IzJAYUjDROiK0JXVMG85xAnGWbFwZkjxVXzrQ== - dependencies: - "@aws-sdk/middleware-sdk-s3" "3.485.0" - "@aws-sdk/types" "3.485.0" - "@smithy/protocol-http" "^3.0.12" - "@smithy/signature-v4" "^2.0.0" - "@smithy/types" "^2.8.0" - tslib "^2.5.0" - "@aws-sdk/signature-v4-multi-region@3.496.0": version "3.496.0" resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.496.0.tgz#0084ad38ab25dc50d5965d31a9c659673d82e86f" @@ -3715,6 +3703,18 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/signature-v4-multi-region@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.502.0.tgz#2d3fab86051eb98a4e4216e0f2f3d957a854b42c" + integrity sha512-NpOXtUXH0ZAgnyI3Y3s2fPrgwbsWoNMwdoXdFZvH0eDzzX80tim7Yuy6dzVA5zrxSzOYs1xjcOhM+4CmM0QZiw== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@smithy/protocol-http" "^3.1.1" + "@smithy/signature-v4" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/signature-v4@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.186.0.tgz#bbd56e71af95548abaeec6307ea1dfe7bd26b4e4" @@ -3967,13 +3967,6 @@ "@aws-sdk/types" "3.6.1" tslib "^1.8.0" -"@aws-sdk/util-arn-parser@3.465.0": - version "3.465.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.465.0.tgz#2896f6b06f69770378586853c97a0f283cbb2e20" - integrity sha512-zOJ82vzDJFqBX9yZBlNeHHrul/kpx/DCoxzW5UBbZeb26kfV53QhMSoEmY8/lEbBqlqargJ/sgRC845GFhHNQw== - dependencies: - tslib "^2.5.0" - "@aws-sdk/util-arn-parser@3.495.0": version "3.495.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.495.0.tgz#539f2d6dfef343a80324348f1f9a1b7eed2390f3" @@ -4143,6 +4136,16 @@ "@smithy/types" "^2.8.0" tslib "^2.5.0" +"@aws-sdk/util-format-url@3.502.0": + version "3.502.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.502.0.tgz#3356d2d38b1342f81117bebe630ae378840366cc" + integrity sha512-4+0zBD0ZIJqtTzSE6VRruRwUx3lG+is8Egv+LN99X5y7i6OdrS9ePYHbCJ9FxkzTThgbkUq6k2W7psEDYvn4VA== + dependencies: + "@aws-sdk/types" "3.502.0" + "@smithy/querystring-builder" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/util-hex-encoding@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.186.0.tgz#7ed58b923997c6265f4dce60c8704237edb98895" From 8520d1e62a8b6eec7e0fcc4d1d6c301d51545378 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:43:21 -0500 Subject: [PATCH 14/24] Bump chromedriver from 120.0.1 to 121.0.0 (#2211) Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 120.0.1 to 121.0.0. - [Commits](https://github.com/giggio/node-chromedriver/compare/120.0.1...121.0.0) --- updated-dependencies: - dependency-name: chromedriver dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 2e2a4c1249..e560932034 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "devDependencies": { "@bahmutov/cypress-esbuild-preprocessor": "^2.2.0", "@cypress-audit/pa11y": "^1.3.0", - "chromedriver": "^120.0.1", + "chromedriver": "^121.0.0", "cypress": "^12.16.0", "cypress-pipe": "^2.0.0", "danger": "^11.2.6", diff --git a/yarn.lock b/yarn.lock index 81ae7cd7c5..6d9de3a6d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14100,12 +14100,12 @@ axios@0.26.0: dependencies: follow-redirects "^1.14.8" -axios@^1.0.0, axios@^1.1.3, axios@^1.6.0, axios@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== +axios@^1.0.0, axios@^1.1.3, axios@^1.6.2, axios@^1.6.5: + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.4" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -15401,13 +15401,13 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -chromedriver@^120.0.1: - version "120.0.1" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-120.0.1.tgz#71604e46a463ab133c3b8b7269dba8ac8fd82b43" - integrity sha512-ETTJlkibcAmvoKsaEoq2TFqEsJw18N0O9gOQZX6Uv/XoEiOV8p+IZdidMeIRYELWJIgCZESvlOx5d1QVnB4v0w== +chromedriver@^121.0.0: + version "121.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-121.0.0.tgz#f8d11dce8e5ce4b6ad75e2b84eed17a0eecabfb9" + integrity sha512-ZIKEdZrQAfuzT/RRofjl8/EZR99ghbdBXNTOcgJMKGP6N/UL6lHUX4n6ONWBV18pDvDFfQJ0x58h5AdOaXIOMw== dependencies: "@testim/chrome-version" "^1.1.4" - axios "^1.6.0" + axios "^1.6.5" compare-versions "^6.1.0" extract-zip "^2.0.1" https-proxy-agent "^5.0.1" @@ -19140,11 +19140,16 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.0.0, follow-redirects@^1.14.8, follow-redirects@^1.15.0: +follow-redirects@^1.0.0, follow-redirects@^1.14.8: version "1.15.4" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== +follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" From 673258fe8b050b509f465a7684a4cf90df3689dd Mon Sep 17 00:00:00 2001 From: Mojo Talantikite Date: Mon, 5 Feb 2024 15:03:48 -0500 Subject: [PATCH 15/24] Add updated ClamAV to layer (#2224) * add in libs for avdownload * move system lib64 into lib * add libxcrypt-compat * trigger layer build * upload-artifact v4 on clamav layer * point to this repo * adding curl * debug for now * missing lib * cleanup * remove debug and +libcrypt.so.1 * more libs * use last version * move to amazon linux 2023 and LTS clamav * remove old build script * remove old build script * add missing freshclam.conf * re-add promote --- .github/workflows/deploy-infra-to-env.yml | 10 ++++ .github/workflows/deploy.yml | 25 ++++++++- .github/workflows/promote.yml | 23 +++++++- services/uploads/src/avLayer/build/build.sh | 56 +++++++++++++++++++ .../uploads/src/avLayer/build/freshclam.conf | 2 + services/uploads/src/avLayer/dockerbuild.sh | 5 ++ 6 files changed, 118 insertions(+), 3 deletions(-) create mode 100755 services/uploads/src/avLayer/build/build.sh create mode 100644 services/uploads/src/avLayer/build/freshclam.conf create mode 100755 services/uploads/src/avLayer/dockerbuild.sh diff --git a/.github/workflows/deploy-infra-to-env.yml b/.github/workflows/deploy-infra-to-env.yml index 91b9ca98cf..00ee028f2b 100644 --- a/.github/workflows/deploy-infra-to-env.yml +++ b/.github/workflows/deploy-infra-to-env.yml @@ -172,6 +172,16 @@ jobs: stage-name: ${{ inputs.stage_name }} changed-services: ${{ inputs.changed_services }} + - uses: actions/download-artifact@v4 + with: + name: lambda-layers-clamav + path: ./services/uploads/lambda-layers-clamav + + - name: Unzip clamav layer + run: | + unzip -d ./services/uploads/lambda-layers-clamav ./services/uploads/lambda-layers-clamav/lambda_layer.zip + rm -f ./services/uploads/lambda-layers-clamav/lambda_layer.zip + - name: deploy uploads id: deploy-uploads env: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 91164a872a..34b0b64e51 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -249,9 +249,30 @@ jobs: name: lambda-layers-prisma-client-engine path: ./services/app-api/lambda-layers-prisma-client-engine + build-clamav-layer: + name: build - clamav layer + runs-on: ubuntu-20.04 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: yarn + + - name: Prepare ClamAV layer + working-directory: services/uploads/src/avLayer + run: ./dockerbuild.sh + + - uses: actions/upload-artifact@v4 + with: + name: lambda-layers-clamav + path: ./services/uploads/src/avLayer/build/lambda_layer.zip + deploy-infra: - needs: [begin-deployment] - uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@main + needs: [begin-deployment, build-clamav-layer] + uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@mt-add-libcurl with: environment: dev stage_name: ${{ needs.begin-deployment.outputs.stage-name}} diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index 7be91378a8..53c29dd055 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -117,8 +117,29 @@ jobs: name: lambda-layers-prisma-client-engine path: ./services/app-api/lambda-layers-prisma-client-engine + build-clamav-layer: + name: build - clamav layer + runs-on: ubuntu-20.04 + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: yarn + + - name: Prepare ClamAV layer + working-directory: services/uploads/src/avLayer + run: ./dockerbuild.sh + + - uses: actions/upload-artifact@v4 + with: + name: lambda-layers-clamav + path: ./services/uploads/src/avLayer/build/lambda_layer.zip + promote-infra-dev: - needs: [build-prisma-client-lambda-layer, unit-tests] + needs: [build-prisma-client-lambda-layer, build-clamav-layer, unit-tests] uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@main with: environment: dev diff --git a/services/uploads/src/avLayer/build/build.sh b/services/uploads/src/avLayer/build/build.sh new file mode 100755 index 0000000000..addc74f5c5 --- /dev/null +++ b/services/uploads/src/avLayer/build/build.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -e + +echo "building clamav lambda layer..." +uname -m +rm -rf bin +rm -rf lib +rm lambda_layer.zip || true + +dnf update -y +dnf install -y cpio yum-utils zip + +# extract binaries for clamav +mkdir -p /tmp/build +pushd /tmp/build + +# Install latest clamav from clamav.net +curl -L --output clamav-1.0.4.linux.x86_64.rpm https://www.clamav.net/downloads/production/clamav-1.0.4.linux.x86_64.rpm +rpm2cpio clamav-*.rpm | cpio -vimd + +# Download other package dependencies +dnf download -x \*i686 --archlist=x86_64 json-c pcre2 libtool-ltdl libxml2 bzip2-libs xz-libs gnutls nettle libcurl libnghttp2 libidn2 libssh2 openldap libffi krb5-libs keyutils-libs libunistring cyrus-sasl-lib nss nspr libselinux openssl-libs +rpm2cpio json-c*.rpm | cpio -vimd +rpm2cpio pcre*.rpm | cpio -vimd +rpm2cpio libtool-ltdl*.rpm | cpio -vimd +rpm2cpio libxml2*.rpm | cpio -vimd +rpm2cpio bzip2-libs*.rpm | cpio -vimd +rpm2cpio xz-libs*.rpm | cpio -vimd +rpm2cpio gnutls*.rpm | cpio -vimd +rpm2cpio nettle*.rpm | cpio -vimd +rpm2cpio libcurl*.rpm | cpio -vimd +rpm2cpio libnghttp2*.rpm | cpio -vimd +rpm2cpio libidn2*.rpm | cpio -vimd +rpm2cpio libssh2*.rpm | cpio -vimd +rpm2cpio openldap*.rpm | cpio -vimd +rpm2cpio libffi*.rpm | cpio -vimd +rpm2cpio krb5-libs*.rpm | cpio -vimd +rpm2cpio keyutils-libs*.rpm | cpio -vimd +rpm2cpio libunistring*.rpm | cpio -vimd +rpm2cpio cyrus-sasl-lib*.rpm | cpio -vimd +rpm2cpio nss*.rpm | cpio -vimd +rpm2cpio nspr*.rpm | cpio -vimd +rpm2cpio libselinux*.rpm | cpio -vimd +rpm2cpio openssl-libs*.rpm | cpio -vimd + +popd + +mkdir -p bin lib + +cp /tmp/build/usr/local/bin/clamscan /tmp/build/usr/local/bin/clamdscan /tmp/build/usr/local/bin/freshclam bin/. +cp -R /tmp/build/usr/lib64/* lib/. +cp -R /tmp/build/usr/local/lib64/* lib/. +cp freshclam.conf bin/freshclam.conf + +zip -r9 lambda_layer.zip bin +zip -r9 lambda_layer.zip lib \ No newline at end of file diff --git a/services/uploads/src/avLayer/build/freshclam.conf b/services/uploads/src/avLayer/build/freshclam.conf new file mode 100644 index 0000000000..b2e6fbf18e --- /dev/null +++ b/services/uploads/src/avLayer/build/freshclam.conf @@ -0,0 +1,2 @@ +DatabaseMirror database.clamav.net +CompressLocalDatabase yes diff --git a/services/uploads/src/avLayer/dockerbuild.sh b/services/uploads/src/avLayer/dockerbuild.sh new file mode 100755 index 0000000000..982ec62f23 --- /dev/null +++ b/services/uploads/src/avLayer/dockerbuild.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -e +docker pull amazonlinux:2023 +docker run --rm --platform linux/x86_64 -v `pwd`/build:/opt/app amazonlinux:2023 /bin/bash -c "cd /opt/app && ./build.sh" \ No newline at end of file From a22f3306ed3dedfb0827cbef70845b8134ce00d9 Mon Sep 17 00:00:00 2001 From: Mojo Talantikite Date: Mon, 5 Feb 2024 15:23:41 -0500 Subject: [PATCH 16/24] move sha to main (#2235) --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 34b0b64e51..ce92b60b9b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -272,7 +272,7 @@ jobs: deploy-infra: needs: [begin-deployment, build-clamav-layer] - uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@mt-add-libcurl + uses: Enterprise-CMCS/managed-care-review/.github/workflows/deploy-infra-to-env.yml@main with: environment: dev stage_name: ${{ needs.begin-deployment.outputs.stage-name}} From 23fd4ea75db555a4d44b40fc1a6f6c468e9d6516 Mon Sep 17 00:00:00 2001 From: ruizajtruss <111928238+ruizajtruss@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:00:20 -0800 Subject: [PATCH 17/24] MCR-3813 Restricting rate access to each respective state (#2228) * rendering error page if the user state code does not match the rate state code * added new restriction to rate fetch that checks state code * added error page for forbidden errors * added error handling for non-matching state codes * error handling * added missing null operator --- .../app-api/src/resolvers/rate/fetchRate.ts | 22 ++++++++++++++- .../src/pages/Errors/ErrorForbiddenPage.tsx | 28 +++++++++++++++++++ .../app-web/src/pages/RateEdit/RateEdit.tsx | 13 ++++++++- .../RateSummary/RateSummary.tsx | 13 ++++++++- 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 services/app-web/src/pages/Errors/ErrorForbiddenPage.tsx diff --git a/services/app-api/src/resolvers/rate/fetchRate.ts b/services/app-api/src/resolvers/rate/fetchRate.ts index 90357d8ed4..e3a61991e3 100644 --- a/services/app-api/src/resolvers/rate/fetchRate.ts +++ b/services/app-api/src/resolvers/rate/fetchRate.ts @@ -4,9 +4,12 @@ import { setSuccessAttributesOnActiveSpan, } from '../attributeHelper' import { NotFoundError } from '../../postgres' -import type { QueryResolvers } from '../../gen/gqlServer' +import type { QueryResolvers, State } from '../../gen/gqlServer' import type { Store } from '../../postgres' import { GraphQLError } from 'graphql' +import { isStateUser } from '../../domain-models' +import { logError } from '../../logger' +import { ForbiddenError } from 'apollo-server-core' export function fetchRateResolver(store: Store): QueryResolvers['fetchRate'] { return async (_parent, { input }, context) => { @@ -35,6 +38,23 @@ export function fetchRateResolver(store: Store): QueryResolvers['fetchRate'] { }) } + if (isStateUser(user)) { + const stateForCurrentUser: State['code'] = user.stateCode + if (rateWithHistory.stateCode !== stateForCurrentUser) { + logError( + 'fetchRate', + 'State users are not authorized to fetch rate data from a different state.' + ) + setErrorAttributesOnActiveSpan( + 'State users are not authorized to fetch rate data from a different state.', + span + ) + throw new ForbiddenError( + 'State users are not authorized to fetch rate data from a different state.' + ) + } + } + setSuccessAttributesOnActiveSpan(span) return { rate: rateWithHistory } } diff --git a/services/app-web/src/pages/Errors/ErrorForbiddenPage.tsx b/services/app-web/src/pages/Errors/ErrorForbiddenPage.tsx new file mode 100644 index 0000000000..7841f4448b --- /dev/null +++ b/services/app-web/src/pages/Errors/ErrorForbiddenPage.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import styles from './Errors.module.scss' +import { GridContainer } from '@trussworks/react-uswds' +import { PageHeading } from '../../components' + +interface ForbiddenErrorPageProps { + errorMsg?: string +} + +export const ErrorForbiddenPage = ({ + errorMsg, +}: ForbiddenErrorPageProps): React.ReactElement => { + return ( +
+ + Forbidden + {errorMsg ? ( +

{errorMsg}

+ ) : ( +

+ You do not have permission to view the requested file or + resource. +

+ )} +
+
+ ) +} diff --git a/services/app-web/src/pages/RateEdit/RateEdit.tsx b/services/app-web/src/pages/RateEdit/RateEdit.tsx index 2225f2c412..d66a3127ea 100644 --- a/services/app-web/src/pages/RateEdit/RateEdit.tsx +++ b/services/app-web/src/pages/RateEdit/RateEdit.tsx @@ -4,6 +4,8 @@ import { useFetchRateQuery } from '../../gen/gqlClient' import { GridContainer } from '@trussworks/react-uswds' import { Loading } from '../../components' import { GenericErrorPage } from '../Errors/GenericErrorPage' +import { ErrorForbiddenPage } from '../Errors/ErrorForbiddenPage' +import { Error404 } from '../Errors/Error404Page' type RouteParams = { id: string @@ -35,7 +37,16 @@ export const RateEdit = (): React.ReactElement => { ) } else if (error || !rate) { - return + //error handling for a state user that tries to access rates for a different state + if (error?.graphQLErrors[0]?.extensions?.code === 'FORBIDDEN') { + return ( + + ) + } else if (error?.graphQLErrors[0]?.extensions?.code === 'NOT_FOUND') { + return + } else { + return + } } if (rate.status !== 'UNLOCKED') { diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx index fc0855602f..3bc21a1f13 100644 --- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx +++ b/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx @@ -10,6 +10,8 @@ import { GenericErrorPage } from '../../Errors/GenericErrorPage' import { RoutesRecord } from '../../../constants' import { SingleRateSummarySection } from '../../../components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection' import { useAuth } from '../../../contexts/AuthContext' +import { ErrorForbiddenPage } from '../../Errors/ErrorForbiddenPage' +import { Error404 } from '../../Errors/Error404Page' type RouteParams = { id: string @@ -50,7 +52,16 @@ export const RateSummary = (): React.ReactElement => { ) } else if (error || !rate || !currentRateRev?.formData) { - return + //error handling for a state user that tries to access rates for a different state + if (error?.graphQLErrors[0]?.extensions?.code === 'FORBIDDEN') { + return ( + + ) + } else if (error?.graphQLErrors[0]?.extensions?.code === 'NOT_FOUND') { + return + } else { + return + } } //Redirecting a state user to the edit page if rate is unlocked From b995962d30630d32399a99536bc7c4578b303c71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:00:11 -0500 Subject: [PATCH 18/24] Bump @aws-sdk/client-lambda from 3.496.0 to 3.507.0 (#2237) Bumps [@aws-sdk/client-lambda](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-lambda) from 3.496.0 to 3.507.0. - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-lambda/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.507.0/clients/client-lambda) --- updated-dependencies: - dependency-name: "@aws-sdk/client-lambda" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 242 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 223 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6d9de3a6d1..3cdea43d99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1142,25 +1142,25 @@ tslib "^2.0.0" "@aws-sdk/client-lambda@^3.241.0", "@aws-sdk/client-lambda@^3.485.0", "@aws-sdk/client-lambda@^3.496.0": - version "3.496.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-lambda/-/client-lambda-3.496.0.tgz#66c7534377e8ff17284f9a69eb684497da9b153a" - integrity sha512-HsypBL5BIxK/2MPZ7mKsAypEn98Jws5kWKmOzVACOvboyvpgaDgrFqubNWkHPWlwg8vJBjixhrQpHxp1vzeVUw== + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-lambda/-/client-lambda-3.507.0.tgz#d9685ac32e68c9fd5580e118e53029c1329e2b6e" + integrity sha512-hJmG+CAuG87cNdxNE5LNimE6+nrfzOfRO2LEeS6WUnhvWLsw8YMHMYoKuQCvhdruP9CyBWklRfjxUp/n94ajuA== dependencies: "@aws-crypto/sha256-browser" "3.0.0" "@aws-crypto/sha256-js" "3.0.0" - "@aws-sdk/client-sts" "3.496.0" + "@aws-sdk/client-sts" "3.507.0" "@aws-sdk/core" "3.496.0" - "@aws-sdk/credential-provider-node" "3.496.0" - "@aws-sdk/middleware-host-header" "3.496.0" - "@aws-sdk/middleware-logger" "3.496.0" - "@aws-sdk/middleware-recursion-detection" "3.496.0" - "@aws-sdk/middleware-signing" "3.496.0" - "@aws-sdk/middleware-user-agent" "3.496.0" - "@aws-sdk/region-config-resolver" "3.496.0" - "@aws-sdk/types" "3.496.0" - "@aws-sdk/util-endpoints" "3.496.0" - "@aws-sdk/util-user-agent-browser" "3.496.0" - "@aws-sdk/util-user-agent-node" "3.496.0" + "@aws-sdk/credential-provider-node" "3.507.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-signing" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" "@smithy/config-resolver" "^2.1.1" "@smithy/core" "^1.3.1" "@smithy/eventstream-serde-browser" "^2.1.1" @@ -1730,6 +1730,51 @@ "@smithy/util-utf8" "^2.1.1" tslib "^2.5.0" +"@aws-sdk/client-sso-oidc@3.507.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.507.0.tgz#d1357d212d9510146d325ca1e6acd06d5744623b" + integrity sha512-ms5CH2ImhqqCIbo5irxayByuPOlVAmSiqDVfjZKwgIziqng2bVgNZMeKcT6t0bmrcgScEAVnZwY7j/iZTIw73g== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/client-sts" "3.507.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-signing" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + "@aws-sdk/client-sso@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.186.0.tgz#233bdd1312dbf88ef9452f8a62c3c3f1ac580330" @@ -1939,6 +1984,49 @@ "@smithy/util-utf8" "^2.1.1" tslib "^2.5.0" +"@aws-sdk/client-sso@3.507.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.507.0.tgz#90a5de90f662aa680c0ffdc5e04695734ca8afb2" + integrity sha512-pFeaKwqv4tXD6QVxWC2V4N62DUoP3bPSm/mCe2SPhaNjNsmwwA53viUHz/nwxIbs8w4vV44UQsygb0AgKm+HoQ== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + tslib "^2.5.0" + "@aws-sdk/client-sts@3.186.3": version "3.186.3" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.186.3.tgz#1c12355cb9d3cadc64ab74c91c3d57515680dfbd" @@ -2165,7 +2253,7 @@ fast-xml-parser "4.2.5" tslib "^2.5.0" -"@aws-sdk/client-sts@3.504.0", "@aws-sdk/client-sts@^3.410.0": +"@aws-sdk/client-sts@3.504.0": version "3.504.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.504.0.tgz#78a0beaf988ad2647d79c7157083dfd55953f41e" integrity sha512-IESs8FkL7B/uY+ml4wgoRkrr6xYo4PizcNw6JX17eveq1gRBCPKeGMjE6HTDOcIYZZ8rqz/UeuH3JD4UhrMOnA== @@ -2210,6 +2298,51 @@ fast-xml-parser "4.2.5" tslib "^2.5.0" +"@aws-sdk/client-sts@3.507.0", "@aws-sdk/client-sts@^3.410.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.507.0.tgz#0a99b5b04ca8d2e30a52840cc67181b3f2ac990a" + integrity sha512-TOWBe0ApEh32QOib0R+irWGjd1F9wnhbGV5PcB9SakyRwvqwG5MKOfYxG7ocoDqLlaRwzZMidcy/PV8/OEVNKg== + dependencies: + "@aws-crypto/sha256-browser" "3.0.0" + "@aws-crypto/sha256-js" "3.0.0" + "@aws-sdk/core" "3.496.0" + "@aws-sdk/middleware-host-header" "3.502.0" + "@aws-sdk/middleware-logger" "3.502.0" + "@aws-sdk/middleware-recursion-detection" "3.502.0" + "@aws-sdk/middleware-user-agent" "3.502.0" + "@aws-sdk/region-config-resolver" "3.502.0" + "@aws-sdk/types" "3.502.0" + "@aws-sdk/util-endpoints" "3.502.0" + "@aws-sdk/util-user-agent-browser" "3.502.0" + "@aws-sdk/util-user-agent-node" "3.502.0" + "@smithy/config-resolver" "^2.1.1" + "@smithy/core" "^1.3.1" + "@smithy/fetch-http-handler" "^2.4.1" + "@smithy/hash-node" "^2.1.1" + "@smithy/invalid-dependency" "^2.1.1" + "@smithy/middleware-content-length" "^2.1.1" + "@smithy/middleware-endpoint" "^2.4.1" + "@smithy/middleware-retry" "^2.1.1" + "@smithy/middleware-serde" "^2.1.1" + "@smithy/middleware-stack" "^2.1.1" + "@smithy/node-config-provider" "^2.2.1" + "@smithy/node-http-handler" "^2.3.1" + "@smithy/protocol-http" "^3.1.1" + "@smithy/smithy-client" "^2.3.1" + "@smithy/types" "^2.9.1" + "@smithy/url-parser" "^2.1.1" + "@smithy/util-base64" "^2.1.1" + "@smithy/util-body-length-browser" "^2.1.1" + "@smithy/util-body-length-node" "^2.2.1" + "@smithy/util-defaults-mode-browser" "^2.1.1" + "@smithy/util-defaults-mode-node" "^2.1.1" + "@smithy/util-endpoints" "^1.1.1" + "@smithy/util-middleware" "^2.1.1" + "@smithy/util-retry" "^2.1.1" + "@smithy/util-utf8" "^2.1.1" + fast-xml-parser "4.2.5" + tslib "^2.5.0" + "@aws-sdk/client-textract@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/client-textract/-/client-textract-3.6.1.tgz#b8972f53f0353222b4c052adc784291e602be6aa" @@ -2513,6 +2646,23 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-ini@3.507.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.507.0.tgz#c2b9cd1bf172a0057bf0ad888c19ce5450df13f2" + integrity sha512-2CnyduoR9COgd7qH1LPYK8UggGqVs8R4ASDMB5bwGxbg9ZerlStDiHpqvJNNg1k+VlejBr++utxfmHd236XgmQ== + dependencies: + "@aws-sdk/client-sts" "3.507.0" + "@aws-sdk/credential-provider-env" "3.502.0" + "@aws-sdk/credential-provider-process" "3.502.0" + "@aws-sdk/credential-provider-sso" "3.507.0" + "@aws-sdk/credential-provider-web-identity" "3.507.0" + "@aws-sdk/types" "3.502.0" + "@smithy/credential-provider-imds" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-ini@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.6.1.tgz#0da6d9341e621f8e0815814ed017b88e268fbc3d" @@ -2625,6 +2775,24 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-node@3.507.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.507.0.tgz#b6c9f3c2c8294911c4f12e267f16a26e1eba4813" + integrity sha512-tkQnmOLkRBXfMLgDYHzogrqTNdtl0Im0ipzJb2IV5hfM5NoTfCf795e9A9isgwjSP/g/YEU0xQWxa4lq8LRtuA== + dependencies: + "@aws-sdk/credential-provider-env" "3.502.0" + "@aws-sdk/credential-provider-http" "3.503.1" + "@aws-sdk/credential-provider-ini" "3.507.0" + "@aws-sdk/credential-provider-process" "3.502.0" + "@aws-sdk/credential-provider-sso" "3.507.0" + "@aws-sdk/credential-provider-web-identity" "3.507.0" + "@aws-sdk/types" "3.502.0" + "@smithy/credential-provider-imds" "^2.2.1" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-node@3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.6.1.tgz#0055292a4f0f49d053e8dfcc9174d8d2cf6862bb" @@ -2767,6 +2935,19 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-sso@3.507.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.507.0.tgz#e98cf7fad69b4c12aa85c44affe9aae4cc81d796" + integrity sha512-6WBjou52QukFpDi4ezb19bcAx/bM8ge8qnJnRT02WVRmU6zFQ5yLD2fW1MFsbX3cwbey+wSqKd5FGE1Hukd5wQ== + dependencies: + "@aws-sdk/client-sso" "3.507.0" + "@aws-sdk/token-providers" "3.507.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/credential-provider-web-identity@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.186.0.tgz#db43f37f7827b553490dd865dbaa9a2c45f95494" @@ -2817,6 +2998,17 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/credential-provider-web-identity@3.507.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.507.0.tgz#22e028e2dd2a0a927707da1408099bc4f5b7a606" + integrity sha512-f+aGMfazBimX7S06224JRYzGTaMh1uIhfj23tZylPJ05KxTVi5IO1RoqeI/uHLJ+bDOx+JHBC04g/oCdO4kHvw== + dependencies: + "@aws-sdk/client-sts" "3.507.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/eventstream-codec@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/eventstream-codec/-/eventstream-codec-3.186.0.tgz#9da9608866b38179edf72987f2bc3b865d11db13" @@ -3897,6 +4089,18 @@ "@smithy/types" "^2.9.1" tslib "^2.5.0" +"@aws-sdk/token-providers@3.507.0": + version "3.507.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.507.0.tgz#7456cec822a7f59a4b58a2eda1a0ff963c4c3c6b" + integrity sha512-ehOINGjoGJc6Puzon7ev4bXckkaZx18WNgMTNttYJhj3vTpj5LPSQbI/5SS927bEbpGMFz1+hJ6Ra5WGfbTcEQ== + dependencies: + "@aws-sdk/client-sso-oidc" "3.507.0" + "@aws-sdk/types" "3.502.0" + "@smithy/property-provider" "^2.1.1" + "@smithy/shared-ini-file-loader" "^2.3.1" + "@smithy/types" "^2.9.1" + tslib "^2.5.0" + "@aws-sdk/types@3.186.0": version "3.186.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.186.0.tgz#f6fb6997b6a364f399288bfd5cd494bc680ac922" @@ -12219,9 +12423,9 @@ integrity sha512-Abq9fBviLV93OiXMu+f6r0elxCzRwc0RC5f99cU892uBITL44pTvgvEqlRlPRi8EGcO1z7Cp8A4d0s/p3J/+Nw== "@types/node@^16.18.39": - version "16.18.78" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.78.tgz#3d97264128712f2eb59f1f8456bcfc5d56d8105c" - integrity sha512-2poPMDdsGfvhcLmgJZ85QrIfN6z3PijYRMiV0FWIEUiQW/t/lzH7BEm4vN+HMhjZXbtIKssMcAxTcgu4Rm83YA== + version "16.18.79" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.79.tgz#153e25561b271cf87dc1b28d38f98cebd514d788" + integrity sha512-Qd7jdLR5zmnIyMhfDrfPqN5tUCvreVpP3Qrf2oSM+F7SNzlb/MwHISGUkdFHtevfkPJ3iAGyeQI/jsbh9EStgQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" From a8bc69b43a316d448af4469b18217e46abe7ea73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:00:29 -0500 Subject: [PATCH 19/24] Bump @testing-library/jest-dom from 6.1.3 to 6.4.2 (#2236) Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.1.3 to 6.4.2. - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v6.1.3...v6.4.2) --- updated-dependencies: - dependency-name: "@testing-library/jest-dom" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cdea43d99..3589cf887e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30,10 +30,10 @@ dependencies: tunnel "^0.0.6" -"@adobe/css-tools@^4.3.0": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.2.tgz#a6abc715fb6884851fca9dad37fc34739a04fd11" - integrity sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw== +"@adobe/css-tools@^4.3.2": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" + integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== "@ampproject/remapping@^2.1.0": version "2.2.0" @@ -11899,16 +11899,16 @@ pretty-format "^27.0.2" "@testing-library/jest-dom@^6.1.3": - version "6.1.3" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.1.3.tgz#443118c9e4043f96396f120de2c7122504a079c5" - integrity sha512-YzpjRHoCBWPzpPNtg6gnhasqtE/5O4qz8WCwDEaxtfnPO6gkaLrnuXusrGSPyhIGPezr1HM7ZH0CFaUTY9PJEQ== + version "6.4.2" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz#38949f6b63722900e2d75ba3c6d9bf8cffb3300e" + integrity sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw== dependencies: - "@adobe/css-tools" "^4.3.0" + "@adobe/css-tools" "^4.3.2" "@babel/runtime" "^7.9.2" aria-query "^5.0.0" chalk "^3.0.0" css.escape "^1.5.1" - dom-accessibility-api "^0.5.6" + dom-accessibility-api "^0.6.3" lodash "^4.17.15" redent "^3.0.0" @@ -17485,11 +17485,16 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: +dom-accessibility-api@^0.5.9: version "0.5.14" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" From 5867d835cb51dde1b87dea527f08025b1602d144 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:05:40 -0500 Subject: [PATCH 20/24] Bump ts-jest from 29.1.1 to 29.1.2 (#2240) Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.1.1 to 29.1.2. - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.1.1...v29.1.2) --- updated-dependencies: - dependency-name: ts-jest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3589cf887e..0208f9100e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22765,19 +22765,7 @@ jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.0.0, jest-util@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.2.tgz#8a052df8fff2eebe446769fd88814521a517664d" - integrity sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w== - dependencies: - "@jest/types" "^29.6.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-util@^29.7.0: +jest-util@^29.0.0, jest-util@^29.6.2, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== @@ -31016,9 +31004,9 @@ ts-invariant@^0.10.3: tslib "^2.1.0" ts-jest@^29.1.1: - version "29.1.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" - integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== + version "29.1.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" + integrity sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" From 8d281f89585dfdf7749a713889d5dd2e9844ec45 Mon Sep 17 00:00:00 2001 From: MacRae Linton Date: Wed, 7 Feb 2024 15:08:34 -0800 Subject: [PATCH 21/24] log authorizer --- services/app-api/src/handlers/third_party_API_authorizer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/app-api/src/handlers/third_party_API_authorizer.ts b/services/app-api/src/handlers/third_party_API_authorizer.ts index 61b0e58083..ed9cfd8c11 100644 --- a/services/app-api/src/handlers/third_party_API_authorizer.ts +++ b/services/app-api/src/handlers/third_party_API_authorizer.ts @@ -26,8 +26,11 @@ const jwtLib = newJWTLib({ }) export const main: APIGatewayTokenAuthorizerHandler = async ( - event + event, context ): Promise => { + console.log('WHATS COMING IN', JSON.stringify(event)) + console.log('EVENT', JSON.stringify(context)) + const authToken = event.authorizationToken.replace('Bearer ', '') try { // authentication step for validating JWT token From 84ec11caa95eecdc9c284a2d68b11188977dfde4 Mon Sep 17 00:00:00 2001 From: MacRae Linton Date: Wed, 7 Feb 2024 15:09:34 -0800 Subject: [PATCH 22/24] Revert "log authorizer" This reverts commit 8d281f89585dfdf7749a713889d5dd2e9844ec45. --- services/app-api/src/handlers/third_party_API_authorizer.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/app-api/src/handlers/third_party_API_authorizer.ts b/services/app-api/src/handlers/third_party_API_authorizer.ts index ed9cfd8c11..61b0e58083 100644 --- a/services/app-api/src/handlers/third_party_API_authorizer.ts +++ b/services/app-api/src/handlers/third_party_API_authorizer.ts @@ -26,11 +26,8 @@ const jwtLib = newJWTLib({ }) export const main: APIGatewayTokenAuthorizerHandler = async ( - event, context + event ): Promise => { - console.log('WHATS COMING IN', JSON.stringify(event)) - console.log('EVENT', JSON.stringify(context)) - const authToken = event.authorizationToken.replace('Bearer ', '') try { // authentication step for validating JWT token From 9b968a0d4b50025c8b23f9102a6a178d34a0e66a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:27:36 -0500 Subject: [PATCH 23/24] Bump prettier from 3.1.0 to 3.2.5 (#2245) Bumps [prettier](https://github.com/prettier/prettier) from 3.1.0 to 3.2.5. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.1.0...3.2.5) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0208f9100e..477f2b2bb6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27040,9 +27040,9 @@ prettier-linter-helpers@^1.0.0: integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== prettier@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.0.tgz#c6d16474a5f764ea1a4a373c593b779697744d5e" - integrity sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw== + version "3.2.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" + integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== pretty-bytes@^5.3.0, pretty-bytes@^5.4.1, pretty-bytes@^5.6.0: version "5.6.0" From d241965729f059fe6674668eaf5e419b3d9568b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:28:00 -0500 Subject: [PATCH 24/24] Bump @typescript-eslint/parser from 6.19.1 to 6.21.0 (#2242) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.19.1 to 6.21.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.21.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/yarn.lock b/yarn.lock index 477f2b2bb6..ba24a20abe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12772,14 +12772,14 @@ debug "^4.3.4" "@typescript-eslint/parser@^6.5.0": - version "6.19.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.19.1.tgz#68a87bb21afaf0b1689e9cdce0e6e75bc91ada78" - integrity sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ== - dependencies: - "@typescript-eslint/scope-manager" "6.19.1" - "@typescript-eslint/types" "6.19.1" - "@typescript-eslint/typescript-estree" "6.19.1" - "@typescript-eslint/visitor-keys" "6.19.1" + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== + dependencies: + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" "@typescript-eslint/scope-manager@5.33.1": @@ -12806,13 +12806,13 @@ "@typescript-eslint/types" "5.58.0" "@typescript-eslint/visitor-keys" "5.58.0" -"@typescript-eslint/scope-manager@6.19.1": - version "6.19.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.19.1.tgz#2f527ee30703a6169a52b31d42a1103d80acd51b" - integrity sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w== +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== dependencies: - "@typescript-eslint/types" "6.19.1" - "@typescript-eslint/visitor-keys" "6.19.1" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" "@typescript-eslint/type-utils@5.57.0": version "5.57.0" @@ -12839,10 +12839,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.58.0.tgz#54c490b8522c18986004df7674c644ffe2ed77d8" integrity sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g== -"@typescript-eslint/types@6.19.1": - version "6.19.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.19.1.tgz#2d4c9d492a63ede15e7ba7d129bdf7714b77f771" - integrity sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg== +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== "@typescript-eslint/typescript-estree@5.33.1": version "5.33.1" @@ -12883,13 +12883,13 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.19.1": - version "6.19.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.1.tgz#796d88d88882f12e85bb33d6d82d39e1aea54ed1" - integrity sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA== +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== dependencies: - "@typescript-eslint/types" "6.19.1" - "@typescript-eslint/visitor-keys" "6.19.1" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -12947,12 +12947,12 @@ "@typescript-eslint/types" "5.58.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.19.1": - version "6.19.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.1.tgz#2164073ed4fc34a5ff3b5e25bb5a442100454c4c" - integrity sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ== +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== dependencies: - "@typescript-eslint/types" "6.19.1" + "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" "@vendia/serverless-express@^4.3.9":