diff --git a/services/app-api/prisma/migrations/20231031204348_migrate_questions_to_contracts/migration.sql b/services/app-api/prisma/migrations/20231031204348_migrate_questions_to_contracts/migration.sql new file mode 100644 index 0000000000..3eccc0622c --- /dev/null +++ b/services/app-api/prisma/migrations/20231031204348_migrate_questions_to_contracts/migration.sql @@ -0,0 +1,17 @@ +BEGIN; + +ALTER TABLE "Question" ADD COLUMN "contractID" TEXT; + +-- AddForeignKey +ALTER TABLE "Question" ADD CONSTRAINT "Question_contractID_fkey" FOREIGN KEY ("contractID") REFERENCES "ContractTable"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- migrate data over +UPDATE "Question" SET "contractID"="pkgID"; + +-- make not nullable, drop pkgID +ALTER TABLE "Question" + ALTER COLUMN "contractID" SET NOT NULL, + DROP CONSTRAINT "Question_pkgID_fkey", + DROP COLUMN "pkgID"; + +COMMIT; diff --git a/services/app-api/prisma/schema.prisma b/services/app-api/prisma/schema.prisma index c40fbe6611..3f89e14ca5 100644 --- a/services/app-api/prisma/schema.prisma +++ b/services/app-api/prisma/schema.prisma @@ -13,7 +13,6 @@ model HealthPlanPackageTable { stateCode String state State @relation(fields: [stateCode], references: [stateCode]) revisions HealthPlanRevisionTable[] - questions Question[] } model ProtoMigrationsTable { @@ -41,9 +40,11 @@ model ContractTable { mccrsID String? stateCode String - state State @relation(fields: [stateCode], references: [stateCode]) + state State @relation(fields: [stateCode], references: [stateCode]) stateNumber Int + questions Question[] + revisions ContractRevisionTable[] // This relationship is a scam. We never call it in our code but Prisma // requires that there be an inverse to RateRevision.draftContracts which we do use @@ -72,8 +73,8 @@ model ContractRevisionTable { contractID String contract ContractTable @relation(fields: [contractID], references: [id]) - rateRevisions RateRevisionsOnContractRevisionsTable[] - draftRates RateTable[] + rateRevisions RateRevisionsOnContractRevisionsTable[] + draftRates RateTable[] unlockInfoID String? unlockInfo UpdateInfoTable? @relation("unlockContractInfo", fields: [unlockInfoID], references: [id]) @@ -130,21 +131,21 @@ model RateRevisionTable { submitInfoID String? submitInfo UpdateInfoTable? @relation("submitRateInfo", fields: [submitInfoID], references: [id], onDelete: Cascade) - rateType RateType? - rateCapitationType RateCapitationType? - rateDocuments RateDocument[] - supportingDocuments RateSupportingDocument[] - rateDateStart DateTime? @db.Date - rateDateEnd DateTime? @db.Date - rateDateCertified DateTime? @db.Date - amendmentEffectiveDateStart DateTime? @db.Date - amendmentEffectiveDateEnd DateTime? @db.Date - rateProgramIDs String[] - rateCertificationName String? - certifyingActuaryContacts ActuaryContact[] @relation(name: "CertifyingActuaryOnRateRevision") - addtlActuaryContacts ActuaryContact[] @relation(name: "AddtlActuaryOnRateRevision") - actuaryCommunicationPreference ActuaryCommunication? - contractsWithSharedRateRevision ContractTable[] @relation(name: "SharedRateRevisions") + rateType RateType? + rateCapitationType RateCapitationType? + rateDocuments RateDocument[] + supportingDocuments RateSupportingDocument[] + rateDateStart DateTime? @db.Date + rateDateEnd DateTime? @db.Date + rateDateCertified DateTime? @db.Date + amendmentEffectiveDateStart DateTime? @db.Date + amendmentEffectiveDateEnd DateTime? @db.Date + rateProgramIDs String[] + rateCertificationName String? + certifyingActuaryContacts ActuaryContact[] @relation(name: "CertifyingActuaryOnRateRevision") + addtlActuaryContacts ActuaryContact[] @relation(name: "AddtlActuaryOnRateRevision") + actuaryCommunicationPreference ActuaryCommunication? + contractsWithSharedRateRevision ContractTable[] @relation(name: "SharedRateRevisions") } model RateRevisionsOnContractRevisionsTable { @@ -291,12 +292,12 @@ model UserAudit { } model Question { - id String @id @default(uuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - pkgID String - pkg HealthPlanPackageTable @relation(fields: [pkgID], references: [id]) - addedBy User @relation(fields: [addedByUserID], references: [id]) + id String @id @default(uuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + contractID String + contract ContractTable @relation(fields: [contractID], references: [id]) + addedBy User @relation(fields: [addedByUserID], references: [id]) addedByUserID String division Division documents QuestionDocument[] @@ -327,10 +328,10 @@ model QuestionResponse { id String @id @default(uuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - question Question @relation(fields: [questionID], references: [id]) - addedBy User @relation(fields: [addedByUserID], references: [id]) questionID String + question Question @relation(fields: [questionID], references: [id]) addedByUserID String + addedBy User @relation(fields: [addedByUserID], references: [id]) documents QuestionResponseDocument[] } diff --git a/services/app-api/src/domain-models/QuestionsType.ts b/services/app-api/src/domain-models/QuestionsType.ts index 7849aca2ee..448aa0d353 100644 --- a/services/app-api/src/domain-models/QuestionsType.ts +++ b/services/app-api/src/domain-models/QuestionsType.ts @@ -9,7 +9,7 @@ type Document = { type Question = { id: string - pkgID: string + contractID: string createdAt: Date addedBy: CMSUserType documents: Document[] @@ -40,7 +40,7 @@ type CreateQuestionPayload = { } type CreateQuestionInput = { - pkgID: string + contractID: string documents: Document[] } diff --git a/services/app-api/src/postgres/postgresStore.ts b/services/app-api/src/postgres/postgresStore.ts index 13630c3218..5a00ae1a41 100644 --- a/services/app-api/src/postgres/postgresStore.ts +++ b/services/app-api/src/postgres/postgresStore.ts @@ -43,7 +43,7 @@ import { insertManyUsers, } from './user' import { - findAllQuestionsByHealthPlanPackage, + findAllQuestionsByContract, insertQuestion, insertQuestionResponse, } from './questionResponse' @@ -139,9 +139,7 @@ type Store = { user: CMSUserType ) => Promise - findAllQuestionsByHealthPlanPackage: ( - pkgID: string - ) => Promise + findAllQuestionsByContract: (pkgID: string) => Promise insertQuestionResponse: ( questionInput: InsertQuestionResponseArgs, @@ -240,8 +238,8 @@ function NewPostgresStore(client: PrismaClient): Store { findAllUsers: () => findAllUsers(client), insertQuestion: (questionInput, user) => insertQuestion(client, questionInput, user), - findAllQuestionsByHealthPlanPackage: (pkgID) => - findAllQuestionsByHealthPlanPackage(client, pkgID), + findAllQuestionsByContract: (pkgID) => + findAllQuestionsByContract(client, pkgID), insertQuestionResponse: (questionInput, user) => insertQuestionResponse(client, questionInput, user), /** diff --git a/services/app-api/src/postgres/questionResponse/findAllQuestionsByHealthPlanPackage.ts b/services/app-api/src/postgres/questionResponse/findAllQuestionsByContract.ts similarity index 78% rename from services/app-api/src/postgres/questionResponse/findAllQuestionsByHealthPlanPackage.ts rename to services/app-api/src/postgres/questionResponse/findAllQuestionsByContract.ts index bd65b04d1b..9f24fa4f07 100644 --- a/services/app-api/src/postgres/questionResponse/findAllQuestionsByHealthPlanPackage.ts +++ b/services/app-api/src/postgres/questionResponse/findAllQuestionsByContract.ts @@ -4,17 +4,15 @@ import type { Question, QuestionResponseType, } from '../../domain-models' -import type { StoreError } from '../storeError' -import { convertPrismaErrorToStoreError } from '../storeError' -export async function findAllQuestionsByHealthPlanPackage( +export async function findAllQuestionsByContract( client: PrismaClient, - pkgID: string -): Promise { + contractID: string +): Promise { try { const findResult = await client.question.findMany({ where: { - pkgID: pkgID, + contractID: contractID, }, include: { documents: { @@ -49,6 +47,10 @@ export async function findAllQuestionsByHealthPlanPackage( return questions } catch (e: unknown) { - return convertPrismaErrorToStoreError(e) + if (e instanceof Error) { + return e + } + console.error('Unknown object thrown by Prisma', e) + return new Error('Unknown object thrown by Prisma') } } diff --git a/services/app-api/src/postgres/questionResponse/index.ts b/services/app-api/src/postgres/questionResponse/index.ts index 5c5946411c..d06428f207 100644 --- a/services/app-api/src/postgres/questionResponse/index.ts +++ b/services/app-api/src/postgres/questionResponse/index.ts @@ -1,4 +1,4 @@ -export { findAllQuestionsByHealthPlanPackage } from './findAllQuestionsByHealthPlanPackage' +export { findAllQuestionsByContract } from './findAllQuestionsByContract' export { insertQuestion } from './insertQuestion' export { convertToIndexQuestionsPayload } from './questionHelpers' export { insertQuestionResponse } from './insertQuestionResponse' diff --git a/services/app-api/src/postgres/questionResponse/insertQuestion.ts b/services/app-api/src/postgres/questionResponse/insertQuestion.ts index 3399cd7e13..17d730bcdc 100644 --- a/services/app-api/src/postgres/questionResponse/insertQuestion.ts +++ b/services/app-api/src/postgres/questionResponse/insertQuestion.ts @@ -25,9 +25,9 @@ export async function insertQuestion( const result = await client.question.create({ data: { id: uuidv4(), - pkg: { + contract: { connect: { - id: questionInput.pkgID, + id: questionInput.contractID, }, }, addedBy: { diff --git a/services/app-api/src/resolvers/healthPlanPackage/healthPlanPackageResolver.ts b/services/app-api/src/resolvers/healthPlanPackage/healthPlanPackageResolver.ts index 5cc3ae3c11..9e9cc295bb 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/healthPlanPackageResolver.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/healthPlanPackageResolver.ts @@ -3,7 +3,6 @@ import { protoToBase64 } from '../../../../app-web/src/common-code/proto/healthP import statePrograms from '../../../../app-web/src/common-code/data/statePrograms.json' import type { Resolvers } from '../../gen/gqlServer' import type { Store } from '../../postgres' -import { isStoreError } from '../../postgres' import { convertToIndexQuestionsPayload } from '../../postgres/questionResponse' import { logError } from '../../logger' import { setErrorAttributesOnActiveSpan } from '../attributeHelper' @@ -59,15 +58,13 @@ export function healthPlanPackageResolver( } return state }, - questions: async (parent, args, context) => { + questions: async (parent, _args, context) => { const { span } = context const pkgID = parent.id - const result = await store.findAllQuestionsByHealthPlanPackage( - pkgID - ) + const result = await store.findAllQuestionsByContract(pkgID) - if (isStoreError(result)) { - const errMessage = `Issue finding questions of type ${result.code} for package with id: ${pkgID}. Message: ${result.message}` + if (result instanceof Error) { + const errMessage = `Issue finding questions for package with id: ${pkgID}. Message: ${result.message}` logError('indexQuestions', errMessage) setErrorAttributesOnActiveSpan(errMessage, span) throw new GraphQLError(errMessage, { diff --git a/services/app-api/src/resolvers/questionResponse/createQuestion.test.ts b/services/app-api/src/resolvers/questionResponse/createQuestion.test.ts index d7e7e78f1e..a661e8f327 100644 --- a/services/app-api/src/resolvers/questionResponse/createQuestion.test.ts +++ b/services/app-api/src/resolvers/questionResponse/createQuestion.test.ts @@ -13,8 +13,10 @@ import { createDBUsersWithFullData, testCMSUser, } from '../../testHelpers/userHelpers' +import { testLDService } from '../../testHelpers/launchDarklyHelpers' describe('createQuestion', () => { + const mockLDService = testLDService({ ['rates-db-refactor']: true }) const cmsUser = testCMSUser() beforeAll(async () => { //Inserting a new CMS user, with division assigned, in postgres in order to create the question to user relationship. @@ -22,11 +24,14 @@ describe('createQuestion', () => { }) it('returns question data after creation', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const cmsServer = await constructTestPostgresServer({ context: { user: cmsUser, }, + ldService: mockLDService, }) const submittedPkg = await createAndSubmitTestHealthPlanPackage( @@ -41,7 +46,7 @@ describe('createQuestion', () => { expect(createdQuestion.question).toEqual( expect.objectContaining({ id: expect.any(String), - pkgID: submittedPkg.id, + contractID: submittedPkg.id, division: 'DMCO', documents: [ { @@ -54,11 +59,14 @@ describe('createQuestion', () => { ) }) it('allows question creation on UNLOCKED and RESUBMITTED package', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const cmsServer = await constructTestPostgresServer({ context: { user: cmsUser, }, + ldService: mockLDService, }) const submittedPkg = await createAndSubmitTestHealthPlanPackage( @@ -104,7 +112,7 @@ describe('createQuestion', () => { node: expect.objectContaining({ id: expect.any(String), createdAt: expect.any(Date), - pkgID: submittedPkg.id, + contractID: submittedPkg.id, division: 'DMCO', documents: [ { @@ -119,7 +127,7 @@ describe('createQuestion', () => { node: expect.objectContaining({ id: expect.any(String), createdAt: expect.any(Date), - pkgID: submittedPkg.id, + contractID: submittedPkg.id, division: 'DMCO', documents: [ { @@ -144,11 +152,14 @@ describe('createQuestion', () => { ) }) it('returns an error package status is DRAFT', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const cmsServer = await constructTestPostgresServer({ context: { user: cmsUser, }, + ldService: mockLDService, }) const draftPkg = await createTestHealthPlanPackage(stateServer) @@ -157,7 +168,7 @@ describe('createQuestion', () => { query: CREATE_QUESTION, variables: { input: { - pkgID: draftPkg.id, + contractID: draftPkg.id, documents: [ { name: 'Test Question', @@ -175,7 +186,9 @@ describe('createQuestion', () => { ) }) it('returns an error if a state user attempts to create a question for a package', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const submittedPkg = await createAndSubmitTestHealthPlanPackage( stateServer ) @@ -184,7 +197,7 @@ describe('createQuestion', () => { query: CREATE_QUESTION, variables: { input: { - pkgID: submittedPkg.id, + contractID: submittedPkg.id, documents: [ { name: 'Test Question', @@ -202,11 +215,14 @@ describe('createQuestion', () => { ) }) it('returns error on invalid package id', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const cmsServer = await constructTestPostgresServer({ context: { user: cmsUser, }, + ldService: mockLDService, }) await createAndSubmitTestHealthPlanPackage(stateServer) @@ -215,7 +231,7 @@ describe('createQuestion', () => { query: CREATE_QUESTION, variables: { input: { - pkgID: 'invalid-pkg-id', + contractID: 'invalid-pkg-id', documents: [ { name: 'Test Question', @@ -229,7 +245,7 @@ describe('createQuestion', () => { expect(createdQuestion.errors).toBeDefined() expect(assertAnErrorCode(createdQuestion)).toBe('NOT_FOUND') expect(assertAnError(createdQuestion).message).toBe( - `Issue finding a package with id invalid-pkg-id. Message: Package with id invalid-pkg-id does not exist` + `Package with id invalid-pkg-id does not exist` ) }) it('returns error when CMS user division is unassigned', async () => { @@ -237,11 +253,14 @@ describe('createQuestion', () => { divisionAssignment: undefined, }) await createDBUsersWithFullData([cmsUserWithNoDivision]) - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const cmsServer = await constructTestPostgresServer({ context: { user: cmsUserWithNoDivision, }, + ldService: mockLDService, }) await createAndSubmitTestHealthPlanPackage(stateServer) @@ -250,7 +269,7 @@ describe('createQuestion', () => { query: CREATE_QUESTION, variables: { input: { - pkgID: 'invalid-pkg-id', + contractID: 'invalid-pkg-id', documents: [ { name: 'Test Question', diff --git a/services/app-api/src/resolvers/questionResponse/createQuestion.ts b/services/app-api/src/resolvers/questionResponse/createQuestion.ts index 32b6eebc6d..aee2a351bb 100644 --- a/services/app-api/src/resolvers/questionResponse/createQuestion.ts +++ b/services/app-api/src/resolvers/questionResponse/createQuestion.ts @@ -1,11 +1,12 @@ import type { MutationResolvers } from '../../gen/gqlServer' -import { isCMSUser, packageStatus } from '../../domain-models' +import { isCMSUser } from '../../domain-models' import { logError, logSuccess } from '../../logger' import { setErrorAttributesOnActiveSpan, setSuccessAttributesOnActiveSpan, } from '../attributeHelper' import { ForbiddenError, UserInputError } from 'apollo-server-lambda' +import { NotFoundError } from '../../postgres' import type { Store } from '../../postgres' import { isStoreError } from '../../postgres' import { GraphQLError } from 'graphql' @@ -44,10 +45,20 @@ export function createQuestionResolver( } // Return error if package is not found or errors - const result = await store.findHealthPlanPackage(input.pkgID) + const contractResult = await store.findContractWithHistory( + input.contractID + ) + if (contractResult instanceof Error) { + if (contractResult instanceof NotFoundError) { + const errMessage = `Package with id ${input.contractID} does not exist` + logError('createQuestion', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + throw new GraphQLError(errMessage, { + extensions: { code: 'NOT_FOUND' }, + }) + } - if (isStoreError(result)) { - const errMessage = `Issue finding a package of type ${result.code}. Message: ${result.message}` + const errMessage = `Issue finding a package. Message: ${contractResult.message}` logError('createQuestion', errMessage) setErrorAttributesOnActiveSpan(errMessage, span) throw new GraphQLError(errMessage, { @@ -58,19 +69,8 @@ export function createQuestionResolver( }) } - if (result === undefined) { - const errMessage = `Issue finding a package with id ${input.pkgID}. Message: Package with id ${input.pkgID} does not exist` - logError('createQuestion', errMessage) - setErrorAttributesOnActiveSpan(errMessage, span) - throw new GraphQLError(errMessage, { - extensions: { code: 'NOT_FOUND' }, - }) - } - - // Return error if package status is DRAFT - const packageStats = packageStatus(result) - - if (packageStats === 'DRAFT') { + // Return error if package status is DRAFT, contract will have no submitted revisions + if (contractResult.revisions.length === 0) { const errMessage = `Issue creating question for health plan package. Message: Cannot create question for health plan package in DRAFT status` logError('createQuestion', errMessage) setErrorAttributesOnActiveSpan(errMessage, span) diff --git a/services/app-api/src/resolvers/questionResponse/createQuestionResponse.test.ts b/services/app-api/src/resolvers/questionResponse/createQuestionResponse.test.ts index a82028ba8d..dbfbed1364 100644 --- a/services/app-api/src/resolvers/questionResponse/createQuestionResponse.test.ts +++ b/services/app-api/src/resolvers/questionResponse/createQuestionResponse.test.ts @@ -10,8 +10,10 @@ import { createDBUsersWithFullData, testCMSUser, } from '../../testHelpers/userHelpers' +import { testLDService } from '../../testHelpers/launchDarklyHelpers' describe('createQuestionResponse', () => { + const mockLDService = testLDService({ ['rates-db-refactor']: true }) const cmsUser = testCMSUser() beforeAll(async () => { //Inserting a new CMS user, with division assigned, in postgres in order to create the question to user relationship. @@ -19,11 +21,14 @@ describe('createQuestionResponse', () => { }) it('returns question response data', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const cmsServer = await constructTestPostgresServer({ context: { user: cmsUser, }, + ldService: mockLDService, }) const submittedPkg = await createAndSubmitTestHealthPlanPackage( @@ -58,7 +63,9 @@ describe('createQuestionResponse', () => { }) it('returns an error when attempting to create response for a question that does not exist', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const fakeID = 'abc-123' const createdResponse = await stateServer.executeOperation({ @@ -84,11 +91,14 @@ describe('createQuestionResponse', () => { }) it('returns an error if a cms user attempts to create a question response for a package', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const cmsServer = await constructTestPostgresServer({ context: { user: cmsUser, }, + ldService: mockLDService, }) const submittedPkg = await createAndSubmitTestHealthPlanPackage( stateServer diff --git a/services/app-api/src/resolvers/questionResponse/indexQuestions.test.ts b/services/app-api/src/resolvers/questionResponse/indexQuestions.test.ts index e68f222e39..23ff062804 100644 --- a/services/app-api/src/resolvers/questionResponse/indexQuestions.test.ts +++ b/services/app-api/src/resolvers/questionResponse/indexQuestions.test.ts @@ -11,8 +11,11 @@ import { createDBUsersWithFullData, testCMSUser, } from '../../testHelpers/userHelpers' +import { testLDService } from '../../testHelpers/launchDarklyHelpers' describe('indexQuestions', () => { + const mockLDService = testLDService({ ['rates-db-refactor']: true }) + const dmcoCMSUser = testCMSUser({ divisionAssignment: 'DMCO', }) @@ -28,21 +31,26 @@ describe('indexQuestions', () => { }) it('returns package with questions and responses for each division', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const dmcoCMSServer = await constructTestPostgresServer({ context: { user: dmcoCMSUser, }, + ldService: mockLDService, }) const dmcpCMSServer = await constructTestPostgresServer({ context: { user: dmcpCMSUser, }, + ldService: mockLDService, }) const oactCMServer = await constructTestPostgresServer({ context: { user: oactCMSUser, }, + ldService: mockLDService, }) const submittedPkg = await createAndSubmitTestHealthPlanPackage( @@ -117,7 +125,7 @@ describe('indexQuestions', () => { node: expect.objectContaining({ id: expect.any(String), createdAt: expect.any(Date), - pkgID: submittedPkg.id, + contractID: submittedPkg.id, division: 'DMCO', documents: [ { @@ -138,7 +146,7 @@ describe('indexQuestions', () => { node: expect.objectContaining({ id: expect.any(String), createdAt: expect.any(Date), - pkgID: submittedPkg.id, + contractID: submittedPkg.id, division: 'DMCP', documents: [ { @@ -159,7 +167,7 @@ describe('indexQuestions', () => { node: expect.objectContaining({ id: expect.any(String), createdAt: expect.any(Date), - pkgID: submittedPkg.id, + contractID: submittedPkg.id, division: 'OACT', documents: [ { @@ -177,7 +185,9 @@ describe('indexQuestions', () => { ) }) it('returns an error if you are requesting for a different state (403)', async () => { - const stateServer = await constructTestPostgresServer() + const stateServer = await constructTestPostgresServer({ + ldService: mockLDService, + }) const otherStateServer = await constructTestPostgresServer({ context: { user: { @@ -189,11 +199,13 @@ describe('indexQuestions', () => { givenName: 'Aang', }, }, + ldService: mockLDService, }) const cmsServer = await constructTestPostgresServer({ context: { user: dmcoCMSUser, }, + ldService: mockLDService, }) const submittedPkg = await createAndSubmitTestHealthPlanPackage( @@ -206,7 +218,7 @@ describe('indexQuestions', () => { query: INDEX_QUESTIONS, variables: { input: { - pkgID: submittedPkg.id, + contractID: submittedPkg.id, }, }, }) @@ -218,7 +230,9 @@ describe('indexQuestions', () => { ) }) it('returns an error if health plan package does not exist', async () => { - const server = await constructTestPostgresServer() + const server = await constructTestPostgresServer({ + ldService: mockLDService, + }) await createAndSubmitTestHealthPlanPackage(server) @@ -226,7 +240,7 @@ describe('indexQuestions', () => { query: INDEX_QUESTIONS, variables: { input: { - pkgID: 'invalid-pkg-id', + contractID: 'invalid-pkg-id', }, }, }) @@ -234,7 +248,7 @@ describe('indexQuestions', () => { expect(result.errors).toBeDefined() expect(assertAnErrorCode(result)).toBe('NOT_FOUND') expect(assertAnError(result).message).toBe( - 'Issue finding a package with id invalid-pkg-id. Message: Package with id invalid-pkg-id does not exist' + 'Issue finding a contract with id invalid-pkg-id. Message: Contract with id invalid-pkg-id does not exist' ) }) }) diff --git a/services/app-api/src/resolvers/questionResponse/indexQuestions.ts b/services/app-api/src/resolvers/questionResponse/indexQuestions.ts index 8fbb2c1474..c157073bbc 100644 --- a/services/app-api/src/resolvers/questionResponse/indexQuestions.ts +++ b/services/app-api/src/resolvers/questionResponse/indexQuestions.ts @@ -1,7 +1,7 @@ import { isStateUser } from '../../domain-models' import type { QueryResolvers } from '../../gen/gqlServer' +import { NotFoundError } from '../../postgres' import type { Store } from '../../postgres' -import { isStoreError } from '../../postgres' import { logError } from '../../logger' import { setErrorAttributesOnActiveSpan } from '../attributeHelper' import { ForbiddenError, UserInputError } from 'apollo-server-lambda' @@ -13,26 +13,28 @@ export function indexQuestionsResolver( ): QueryResolvers['indexQuestions'] { return async (_parent, { input }, context) => { const { user, span } = context - const pkgResult = await store.findHealthPlanPackage(input.pkgID) - if (isStoreError(pkgResult)) { - const errMessage = `Issue finding a package of type ${pkgResult.code}. Message: ${pkgResult.message}` + const contractResult = await store.findContractWithHistory( + input.contractID + ) + if (contractResult instanceof Error) { + if (contractResult instanceof NotFoundError) { + const errMessage = `Issue finding a contract with id ${input.contractID}. Message: Contract with id ${input.contractID} does not exist` + logError('createQuestion', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + throw new GraphQLError(errMessage, { + extensions: { code: 'NOT_FOUND' }, + }) + } + const errMessage = `Issue finding a package. Message: ${contractResult.message}` logError('createQuestion', errMessage) setErrorAttributesOnActiveSpan(errMessage, span) throw new UserInputError(errMessage) } - if (pkgResult === undefined) { - const errMessage = `Issue finding a package with id ${input.pkgID}. Message: Package with id ${input.pkgID} does not exist` - logError('createQuestion', errMessage) - setErrorAttributesOnActiveSpan(errMessage, span) - throw new GraphQLError(errMessage, { - extensions: { code: 'NOT_FOUND' }, - }) - } - + const contract = contractResult // State users can only view if the state matches - if (isStateUser(user) && pkgResult.stateCode !== user.stateCode) { + if (isStateUser(user) && contract.stateCode !== user.stateCode) { const errMessage = 'User not authorized to fetch data from a different state' logError('indexQuestions', errMessage) @@ -40,12 +42,12 @@ export function indexQuestionsResolver( throw new ForbiddenError(errMessage) } - const questionResult = await store.findAllQuestionsByHealthPlanPackage( - input.pkgID + const questionResult = await store.findAllQuestionsByContract( + input.contractID ) - if (isStoreError(questionResult)) { - const errMessage = `Issue finding questions of type ${questionResult.code}. Message: ${questionResult.message}` + if (questionResult instanceof Error) { + const errMessage = `Issue finding questions. Message: ${questionResult.message}` logError('indexQuestions', errMessage) setErrorAttributesOnActiveSpan(errMessage, span) throw new Error(errMessage) diff --git a/services/app-api/src/testHelpers/gqlHelpers.ts b/services/app-api/src/testHelpers/gqlHelpers.ts index c8765b0f96..f28158f113 100644 --- a/services/app-api/src/testHelpers/gqlHelpers.ts +++ b/services/app-api/src/testHelpers/gqlHelpers.ts @@ -427,8 +427,8 @@ const fetchTestHealthPlanPackageById = async ( const createTestQuestion = async ( server: ApolloServer, - pkgID: string, - questionData?: Omit + contractID: string, + questionData?: Omit ): Promise => { const question = questionData || { documents: [ @@ -442,7 +442,7 @@ const createTestQuestion = async ( query: CREATE_QUESTION, variables: { input: { - pkgID, + contractID, ...question, }, }, @@ -462,13 +462,13 @@ const createTestQuestion = async ( const indexTestQuestions = async ( server: ApolloServer, - pkgID: string + contractID: string ): Promise => { const indexQuestionsResult = await server.executeOperation({ query: INDEX_QUESTIONS, variables: { input: { - pkgID: pkgID, + contractID, }, }, }) diff --git a/services/app-api/src/testHelpers/storeHelpers.ts b/services/app-api/src/testHelpers/storeHelpers.ts index dc0d372f48..bff94b49fa 100644 --- a/services/app-api/src/testHelpers/storeHelpers.ts +++ b/services/app-api/src/testHelpers/storeHelpers.ts @@ -95,8 +95,8 @@ function mockStoreThatErrors(): Store { insertQuestion: async (_ID) => { return genericStoreError }, - findAllQuestionsByHealthPlanPackage: async (_pkgID) => { - return genericStoreError + findAllQuestionsByContract: async (_pkgID) => { + return genericError }, insertQuestionResponse: async (_ID) => { return genericStoreError diff --git a/services/app-graphql/src/mutations/createQuestion.graphql b/services/app-graphql/src/mutations/createQuestion.graphql index 1181885a82..a81a5aadec 100644 --- a/services/app-graphql/src/mutations/createQuestion.graphql +++ b/services/app-graphql/src/mutations/createQuestion.graphql @@ -2,7 +2,7 @@ mutation createQuestion($input: CreateQuestionInput!) { createQuestion(input: $input) { question { id - pkgID + contractID createdAt addedBy { id diff --git a/services/app-graphql/src/queries/fetchHealthPlanPackageWithQuestions.graphql b/services/app-graphql/src/queries/fetchHealthPlanPackageWithQuestions.graphql index 54ef5681e7..340c3222ea 100644 --- a/services/app-graphql/src/queries/fetchHealthPlanPackageWithQuestions.graphql +++ b/services/app-graphql/src/queries/fetchHealthPlanPackageWithQuestions.graphql @@ -1,7 +1,7 @@ fragment questionEdgeFragment on QuestionEdge { node { id - pkgID + contractID createdAt addedBy { id diff --git a/services/app-graphql/src/queries/indexQuestions.graphql b/services/app-graphql/src/queries/indexQuestions.graphql index 2143fa6613..4ed303bae9 100644 --- a/services/app-graphql/src/queries/indexQuestions.graphql +++ b/services/app-graphql/src/queries/indexQuestions.graphql @@ -1,7 +1,7 @@ fragment questionEdgeFragment on QuestionEdge { node { id - pkgID + contractID createdAt addedBy { id diff --git a/services/app-graphql/src/schema.graphql b/services/app-graphql/src/schema.graphql index bbc37cb94f..c46bce7078 100644 --- a/services/app-graphql/src/schema.graphql +++ b/services/app-graphql/src/schema.graphql @@ -308,7 +308,7 @@ type IndexHealthPlanPackagesPayload { input IndexQuestionsInput { "The ID of the package for which to fetch associated questions" - pkgID: ID! + contractID: ID! } type IndexQuestionsPayload { @@ -338,7 +338,7 @@ input DocumentInput { input CreateQuestionInput { "The ID of the package for which to create a question" - pkgID: ID! + contractID: ID! "A list of documents to attach to the question" documents: [DocumentInput!]! # should be Generic Document with sha256 "A note to attach to the question" @@ -664,7 +664,7 @@ QuestionResponse with documents that answer the questions posed by CMS. """ type Question { id: ID! - pkgID: ID! + contractID: ID! createdAt: DateTime! addedBy: CMSUser! documents: [Document!]! diff --git a/services/app-web/src/gqlHelpers/mutationWrappersForUserFriendlyErrors.ts b/services/app-web/src/gqlHelpers/mutationWrappersForUserFriendlyErrors.ts index 7cf27c7f4a..aae2163742 100644 --- a/services/app-web/src/gqlHelpers/mutationWrappersForUserFriendlyErrors.ts +++ b/services/app-web/src/gqlHelpers/mutationWrappersForUserFriendlyErrors.ts @@ -168,7 +168,7 @@ export const createQuestionWrapper = async ( query: FetchHealthPlanPackageWithQuestionsDocument, variables: { input: { - pkgID: newQuestion.pkgID, + pkgID: newQuestion.contractID, }, }, } diff --git a/services/app-web/src/gqlHelpers/useIndexQuestionsQueryWrapper.ts b/services/app-web/src/gqlHelpers/useIndexQuestionsQueryWrapper.ts index 6a4dd2e7a6..b4dea6219d 100644 --- a/services/app-web/src/gqlHelpers/useIndexQuestionsQueryWrapper.ts +++ b/services/app-web/src/gqlHelpers/useIndexQuestionsQueryWrapper.ts @@ -10,7 +10,7 @@ function useIndexQuestionsQueryWrapper(id: string): WrappedFetchResultType { useIndexQuestionsQuery({ variables: { input: { - pkgID: id, + contractID: id, }, }, }) diff --git a/services/app-web/src/pages/QuestionResponse/QATable/QATable.test.tsx b/services/app-web/src/pages/QuestionResponse/QATable/QATable.test.tsx index 0d279d3ddd..fc1d6025b9 100644 --- a/services/app-web/src/pages/QuestionResponse/QATable/QATable.test.tsx +++ b/services/app-web/src/pages/QuestionResponse/QATable/QATable.test.tsx @@ -14,7 +14,7 @@ const cmsUser = mockValidCMSUser() as CmsUser const testQuestionData: QuestionData = { id: 'question-1-id', - pkgID: '15', + contractID: '15', createdAt: new Date('2022-12-23T00:00:00.000Z'), addedBy: cmsUser, documents: [ @@ -133,7 +133,7 @@ it('renders question correctly as a state user', async () => { it('renders multiple documents', async () => { const testQuestionDocLinks: QuestionData = { id: 'question-1-id', - pkgID: '15', + contractID: '15', createdAt: new Date('2022-12-23T00:00:00.000Z'), addedBy: cmsUser, documents: [ diff --git a/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx b/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx index 365bc62fa1..000b887340 100644 --- a/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx +++ b/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx @@ -1,5 +1,5 @@ import styles from './QATable.module.scss' -import React, { useState } from 'react' +import { useState } from 'react' import { CmsUser, Document, @@ -20,7 +20,7 @@ type QuestionDocumentWithLink = { export type QuestionData = { id: string - pkgID: string + contractID: string createdAt: Date addedBy: CmsUser documents: Document[] 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 89183b5556..ad388fe0b5 100644 --- a/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.test.tsx +++ b/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.test.tsx @@ -19,9 +19,11 @@ import { } from '../../../testHelpers/apolloMocks' import { createQuestionNetworkFailure, + createQuestionSuccess, fetchStateHealthPlanPackageWithQuestionsMockSuccess, } from '../../../testHelpers/apolloMocks/questionResponseGQLMock' import { SubmissionSideNav } from '../../SubmissionSideNav' +import { Location } from 'react-router-dom' describe('UploadQuestions', () => { beforeEach(() => { @@ -124,8 +126,77 @@ describe('UploadQuestions', () => { }) }) + it('allows submission with an uploaded doc', async () => { + let testLocation: Location + const { user } = renderWithProviders( + + }> + } + /> + + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + fetchStateHealthPlanPackageWithQuestionsMockSuccess({ + id: '15', + }), + createQuestionSuccess({ + contractID: '15', + documents: [ + { + name: 'testFile.doc', + s3URL: 's3://fake-bucket/fakeS3Key0-testFile.doc/testFile.doc', + }, + ], + }), + fetchStateHealthPlanPackageWithQuestionsMockSuccess({ + id: '15', + }), + ], + }, + routerProvider: { + route: `/submissions/15/question-and-answers/dmco/upload-questions`, + }, + location: (location) => (testLocation = location), + } + ) + + await screen.findByRole('heading', { + name: /Add questions/, + level: 2, + }) + const input = screen.getByLabelText('Upload questions') + expect(input).toBeInTheDocument() + expect(input).toHaveAttribute('accept', ACCEPTED_SUBMISSION_FILE_TYPES) + await userEvent.upload(input, [TEST_DOC_FILE]) + await waitFor(() => { + expect(screen.getByText(TEST_DOC_FILE.name)).toBeInTheDocument() + }) + + // OK, this document has been uploaded with an S3Key of the current timestamp + // is that serving us at all? gives us unique keys which is good. + // makes it pretty difficult to create the correct mock, since everything needs to match exactly. + + await user.click( + await screen.findByRole('button', { name: /Add questions/ }) + ) + + await waitFor(() => + expect(testLocation.pathname).toBe( + `/submissions/15/question-and-answers` + ) + ) + }) + it('displays form validation error if attempting to add question with zero files', async () => { - renderWithProviders( + const { user } = renderWithProviders( }> { }) expect(continueButton).not.toHaveAttribute('aria-disabled') - await continueButton.click() + await user.click(continueButton) await waitFor(() => { expect( @@ -170,7 +241,7 @@ describe('UploadQuestions', () => { }) it('displays file upload alert if attempting to add question with all invalid files', async () => { - renderWithProviders( + const { user } = renderWithProviders( }> { }) const targetEl = screen.getByTestId('file-input-droptarget') - await dragAndDrop(targetEl, [TEST_PNG_FILE]) + dragAndDrop(targetEl, [TEST_PNG_FILE]) expect( await screen.findByText('This is not a valid file type.') ).toBeInTheDocument() expect(continueButton).not.toHaveAttribute('aria-disabled') - await continueButton.click() + await user.click(continueButton) expect( await screen.findAllByText('You must upload at least one document') @@ -284,7 +355,7 @@ describe('UploadQuestions', () => { }) it('displays api error if createQuestion fails', async () => { - renderWithProviders( + const { user } = renderWithProviders( }> { fetchStateHealthPlanPackageWithQuestionsMockSuccess({ id: '15', }), - createQuestionNetworkFailure(), + createQuestionNetworkFailure({ + contractID: '15', + documents: [ + { + name: 'testFile.doc', + s3URL: 's3://fake-bucket/fakeS3Key0-testFile.doc/testFile.doc', + }, + ], + }), ], }, } @@ -322,7 +401,7 @@ describe('UploadQuestions', () => { await screen.findByText(TEST_DOC_FILE.name) await screen.findByText(/1 complete/) - createQuestionButton.click() + await user.click(createQuestionButton) await screen.findByTestId('error-alert') expect( diff --git a/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx b/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx index 17caf1e62f..73ec54dd9e 100644 --- a/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx +++ b/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx @@ -80,7 +80,7 @@ export const UploadQuestions = () => { }) const input: CreateQuestionInput = { - pkgID: id as string, + contractID: id as string, documents: questionDocs, } diff --git a/services/app-web/src/pages/StateSubmission/RateDetails/RateDetails.test.tsx b/services/app-web/src/pages/StateSubmission/RateDetails/RateDetails.test.tsx index 7d40d8eb4e..2d1e9ca4fe 100644 --- a/services/app-web/src/pages/StateSubmission/RateDetails/RateDetails.test.tsx +++ b/services/app-web/src/pages/StateSubmission/RateDetails/RateDetails.test.tsx @@ -111,7 +111,7 @@ describe('RateDetails', () => { ).not.toBeInTheDocument() const requiredLabels = await screen.findAllByText('Required') expect(requiredLabels).toHaveLength(6) - const optionalLabels = await screen.queryAllByText('Optional') + const optionalLabels = screen.queryAllByText('Optional') expect(optionalLabels).toHaveLength(1) }) diff --git a/services/app-web/src/testHelpers/apolloMocks/questionResponseDataMocks.ts b/services/app-web/src/testHelpers/apolloMocks/questionResponseDataMocks.ts index b9329a539f..d10bcd0e6f 100644 --- a/services/app-web/src/testHelpers/apolloMocks/questionResponseDataMocks.ts +++ b/services/app-web/src/testHelpers/apolloMocks/questionResponseDataMocks.ts @@ -1,7 +1,7 @@ import { CmsUser, IndexQuestionsPayload, StateUser } from '../../gen/gqlClient' import { mockValidCMSUser, mockValidUser } from './userGQLMock' -function mockQuestionsPayload(pkgID: string): IndexQuestionsPayload { +function mockQuestionsPayload(contractID: string): IndexQuestionsPayload { return { DMCOQuestions: { totalCount: 2, @@ -11,7 +11,7 @@ function mockQuestionsPayload(pkgID: string): IndexQuestionsPayload { node: { __typename: 'Question' as const, id: 'dmco-question-1-id', - pkgID, + contractID, createdAt: new Date('2022-12-15'), addedBy: mockValidCMSUser({ divisionAssignment: 'DMCO', @@ -45,7 +45,7 @@ function mockQuestionsPayload(pkgID: string): IndexQuestionsPayload { node: { __typename: 'Question' as const, id: 'dmco-question-2-id', - pkgID, + contractID, createdAt: new Date('2022-12-18'), addedBy: mockValidCMSUser() as CmsUser, documents: [ @@ -86,7 +86,7 @@ function mockQuestionsPayload(pkgID: string): IndexQuestionsPayload { node: { __typename: 'Question' as const, id: 'dmcp-question-1-id', - pkgID, + contractID, createdAt: new Date('2022-12-15'), addedBy: mockValidCMSUser({ divisionAssignment: 'DMCP', @@ -125,7 +125,7 @@ function mockQuestionsPayload(pkgID: string): IndexQuestionsPayload { node: { __typename: 'Question' as const, id: 'oact-question-1-id', - pkgID, + contractID, createdAt: new Date('2022-12-15'), addedBy: mockValidCMSUser({ divisionAssignment: 'OACT', diff --git a/services/app-web/src/testHelpers/apolloMocks/questionResponseGQLMock.ts b/services/app-web/src/testHelpers/apolloMocks/questionResponseGQLMock.ts index 797985f20d..537548708e 100644 --- a/services/app-web/src/testHelpers/apolloMocks/questionResponseGQLMock.ts +++ b/services/app-web/src/testHelpers/apolloMocks/questionResponseGQLMock.ts @@ -4,7 +4,6 @@ import { CreateQuestionResponseDocument, CreateQuestionInput, CreateQuestionMutation, - Question as QuestionType, QuestionResponse as QuestionResponseType, FetchHealthPlanPackageWithQuestionsDocument, FetchHealthPlanPackageWithQuestionsQuery, @@ -25,8 +24,8 @@ const createQuestionSuccess = ( question?: CreateQuestionInput | Partial ): MockedResponse => { const defaultQuestionInput: CreateQuestionInput = { - dueDate: new Date('11-11-2100'), - pkgID: '123-abc', + // dueDate: new Date('11-11-2100'), + contractID: '123-abc', documents: [ { name: 'Test document', @@ -47,7 +46,7 @@ const createQuestionSuccess = ( createQuestion: { question: { id: 'test123', - pkgID: testInput.pkgID, + contractID: testInput.contractID, createdAt: new Date(), addedBy: mockValidCMSUser(), division: 'DMCO', @@ -60,15 +59,18 @@ const createQuestionSuccess = ( } const createQuestionNetworkFailure = ( - question?: QuestionType | Partial + input: CreateQuestionInput ): MockedResponse => { return { - request: { query: CreateQuestionDocument }, + request: { + query: CreateQuestionDocument, + variables: { input }, + }, error: new Error('A network error occurred'), } } const createQuestionResponseNetworkFailure = ( - question?: QuestionResponseType | Partial + _question?: QuestionResponseType | Partial ): MockedResponse => { return { request: { query: CreateQuestionResponseDocument }, diff --git a/services/app-web/src/testHelpers/jestHelpers.tsx b/services/app-web/src/testHelpers/jestHelpers.tsx index 5e7b2ca483..cd8b68ed16 100644 --- a/services/app-web/src/testHelpers/jestHelpers.tsx +++ b/services/app-web/src/testHelpers/jestHelpers.tsx @@ -47,8 +47,9 @@ const renderWithProviders = ( const { route } = routerProvider const s3Client: S3ClientT = s3Provider ?? testS3Client() + const user = userEvent.setup() - return render( + const renderResult = render( @@ -60,6 +61,10 @@ const renderWithProviders = ( ) + return { + user, + ...renderResult, + } } const WithLocation = ({ diff --git a/services/app-web/src/testHelpers/s3Helpers.ts b/services/app-web/src/testHelpers/s3Helpers.ts index 71e5a934bc..5e80c117b6 100644 --- a/services/app-web/src/testHelpers/s3Helpers.ts +++ b/services/app-web/src/testHelpers/s3Helpers.ts @@ -2,9 +2,11 @@ import { S3ClientT } from '../s3' import { parseKey } from '../common-code/s3URLEncoding' export const testS3Client: () => S3ClientT = () => { + let fakeKeyID = 0 + return { uploadFile: async (file: File): Promise => { - return `${Date.now()}-${file.name}` + return `fakeS3Key${fakeKeyID++}-${file.name}` }, deleteFile: async (filename: string): Promise => { return diff --git a/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts b/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts index c7fe07b6f3..dfedff3bb6 100644 --- a/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts +++ b/services/cypress/integration/stateWorkflow/questionResponse/questionResponse.spec.ts @@ -5,9 +5,10 @@ describe('Q&A', () => { cy.interceptGraphQL() }) - it.skip('can add questions and responses', () => { + it('can add questions and responses', () => { cy.interceptFeatureFlags({ 'cms-questions': true, + 'rates-db-refactor': true }) // Assign Division to CMS user zuko diff --git a/services/cypress/support/apiCommands.ts b/services/cypress/support/apiCommands.ts index e19fa65f83..901ca24d7f 100644 --- a/services/cypress/support/apiCommands.ts +++ b/services/cypress/support/apiCommands.ts @@ -23,6 +23,7 @@ import { CMSUserType, } from '../utils/apollo-test-utils' import { ApolloClient, DocumentNode, NormalizedCacheObject } from '@apollo/client' +import {UnlockedHealthPlanFormDataType} from 'app-web/src/common-code/healthPlanFormDataType'; const createAndSubmitContractOnlyPackage = async ( apolloClient: ApolloClient @@ -47,7 +48,7 @@ const createAndSubmitContractOnlyPackage = async ( ...contractOnlyData(), } - const formDataProto = domainToBase64(fullFormData) + const formDataProto = domainToBase64(fullFormData as UnlockedHealthPlanFormDataType) await apolloClient.mutate({ mutation: UpdateHealthPlanFormDataDocument, @@ -94,7 +95,7 @@ const createAndSubmitContractWithRates = async ( ...contractAndRatesData(), } - const formDataProto = domainToBase64(fullFormData1) + const formDataProto = domainToBase64(fullFormData1 as UnlockedHealthPlanFormDataType) await apolloClient.mutate({ mutation: UpdateHealthPlanFormDataDocument,