From 18f3a52c3d046b13bba558170ada23928259c3df Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Tue, 22 Aug 2023 12:44:16 -0400 Subject: [PATCH 01/16] find all contracts function --- .../findAllContractsWithHistoryByState.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts new file mode 100644 index 0000000000..cb3a520f21 --- /dev/null +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts @@ -0,0 +1,38 @@ +import type { PrismaTransactionType } from '../prismaTypes' +import type { ContractType } from '../../domain-models/contractAndRates' +import { NotFoundError } from '../storeError' +import { parseContractWithHistory } from './parseContractWithHistory' +import { includeFullContract } from './prismaSubmittedContractHelpers' + +async function findAllContractsWithHistoryByState( + client: PrismaTransactionType, + stateCode: string +): Promise | NotFoundError | Error> { + try { + const contracts = await client.contractTable.findMany({ + where: { + stateCode: { + equals: stateCode, + }, + }, + include: includeFullContract, + }) + + if (!contracts) { + const err = `PRISMA ERROR: Cannot find contracts with state code: ${stateCode}` + console.error(err) + return new NotFoundError(err) + } + + const parsedContracts: Array = contracts.map( + (contract) => parseContractWithHistory(contract) + ) + + return parsedContracts + } catch (err) { + console.error('PRISMA ERROR', err) + return err + } +} + +export { findAllContractsWithHistoryByState } From d30e60b29e47bbd5e2cc264334de2ce688304731 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Tue, 22 Aug 2023 15:54:59 -0400 Subject: [PATCH 02/16] Add new function to postgresStore.ts and add LD to resolver. --- services/app-api/src/postgres/contractAndRates/index.ts | 7 ++++++- services/app-api/src/postgres/postgresStore.ts | 7 +++++++ services/app-api/src/resolvers/configureResolvers.ts | 5 ++++- .../resolvers/healthPlanPackage/indexHealthPlanPackages.ts | 4 +++- services/app-api/src/testHelpers/storeHelpers.ts | 5 +++++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/services/app-api/src/postgres/contractAndRates/index.ts b/services/app-api/src/postgres/contractAndRates/index.ts index 345a0db98b..f0eefdd9a8 100644 --- a/services/app-api/src/postgres/contractAndRates/index.ts +++ b/services/app-api/src/postgres/contractAndRates/index.ts @@ -1,7 +1,12 @@ import type { InsertContractArgsType } from './insertContract' import { insertDraftContract } from './insertContract' import { findContractWithHistory } from './findContractWithHistory' +import { findAllContractsWithHistoryByState } from './findAllContractsWithHistoryByState' -export { insertDraftContract, findContractWithHistory } +export { + insertDraftContract, + findContractWithHistory, + findAllContractsWithHistoryByState, +} export type { InsertContractArgsType } diff --git a/services/app-api/src/postgres/postgresStore.ts b/services/app-api/src/postgres/postgresStore.ts index be0224cd3b..090e786f15 100644 --- a/services/app-api/src/postgres/postgresStore.ts +++ b/services/app-api/src/postgres/postgresStore.ts @@ -51,6 +51,7 @@ import type { ContractType } from '../domain-models/contractAndRates' import { insertDraftContract, findContractWithHistory, + findAllContractsWithHistoryByState, } from './contractAndRates' import type { InsertContractArgsType } from './contractAndRates' @@ -137,6 +138,10 @@ type Store = { findContractWithHistory: ( contractID: string ) => Promise + + findAllContractsWithHistoryByState: ( + stateCode: string + ) => Promise | Error> } function NewPostgresStore(client: PrismaClient): Store { @@ -197,6 +202,8 @@ function NewPostgresStore(client: PrismaClient): Store { insertDraftContract: (args) => insertDraftContract(client, args), findContractWithHistory: (args) => findContractWithHistory(client, args), + findAllContractsWithHistoryByState: (args) => + findAllContractsWithHistoryByState(client, args), } } diff --git a/services/app-api/src/resolvers/configureResolvers.ts b/services/app-api/src/resolvers/configureResolvers.ts index 38eef5de0e..f1c63ec746 100644 --- a/services/app-api/src/resolvers/configureResolvers.ts +++ b/services/app-api/src/resolvers/configureResolvers.ts @@ -42,7 +42,10 @@ export function configureResolvers( store, launchDarkly ), - indexHealthPlanPackages: indexHealthPlanPackagesResolver(store), + indexHealthPlanPackages: indexHealthPlanPackagesResolver( + store, + launchDarkly + ), indexUsers: indexUsersResolver(store), indexQuestions: indexQuestionsResolver(store), fetchEmailSettings: fetchEmailSettingsResolver( diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts index 1b43f8f3a0..ceed506dfb 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts @@ -12,6 +12,7 @@ import { setResolverDetailsOnActiveSpan, setSuccessAttributesOnActiveSpan, } from '../attributeHelper' +import type { LDService } from '../../launchDarkly/launchDarkly' const validateAndReturnHealthPlanPackages = ( results: HealthPlanPackageType[] | StoreError, @@ -40,7 +41,8 @@ const validateAndReturnHealthPlanPackages = ( } export function indexHealthPlanPackagesResolver( - store: Store + store: Store, + launchDarkly: LDService ): QueryResolvers['indexHealthPlanPackages'] { return async (_parent, _args, context) => { const { user, span } = context diff --git a/services/app-api/src/testHelpers/storeHelpers.ts b/services/app-api/src/testHelpers/storeHelpers.ts index 38dd6a67fa..b6534c6232 100644 --- a/services/app-api/src/testHelpers/storeHelpers.ts +++ b/services/app-api/src/testHelpers/storeHelpers.ts @@ -107,6 +107,11 @@ function mockStoreThatErrors(): Store { 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' ) }, + findAllContractsWithHistoryByState: async (_ID) => { + return new Error( + 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' + ) + }, } } From 3ced6442a3f67acb9e8bbd065ccfd1094ab7a898 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 23 Aug 2023 10:27:37 -0400 Subject: [PATCH 03/16] Add find all contracts by submittedAt. --- ...lContractsWithHistoryBySubmittedAt.test.ts | 3 ++ ...indAllContractsWithHistoryBySubmittedAt.ts | 45 +++++++++++++++++++ .../src/postgres/contractAndRates/index.ts | 2 + 3 files changed, 50 insertions(+) create mode 100644 services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.test.ts create mode 100644 services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.ts diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.test.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.test.ts new file mode 100644 index 0000000000..04834b8aaf --- /dev/null +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.test.ts @@ -0,0 +1,3 @@ +describe('findAllContractsWithHistoryBySubmittedAt', () => { + it.todo('returns only contracts that have been submitted') +}) diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.ts new file mode 100644 index 0000000000..21ec6af773 --- /dev/null +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.ts @@ -0,0 +1,45 @@ +import type { PrismaTransactionType } from '../prismaTypes' +import type { ContractType } from '../../domain-models/contractAndRates' +import { NotFoundError } from '../storeError' +import { parseContractWithHistory } from './parseContractWithHistory' +import { includeFullContract } from './prismaSubmittedContractHelpers' + +async function findAllContractsWithHistoryBySubmittedAt( + client: PrismaTransactionType, + stateCode: string +): Promise | NotFoundError | Error> { + try { + const contracts = await client.contractTable.findMany({ + where: { + revisions: { + some: { + submitInfoID: { + not: null, + }, + }, + }, + stateCode: { + not: 'AS', // exclude test state as per ADR 019 + }, + }, + include: includeFullContract, + }) + + if (!contracts) { + const err = `PRISMA ERROR: Cannot find contracts with state code: ${stateCode}` + console.error(err) + return new NotFoundError(err) + } + + const parsedContracts: Array = contracts.map( + (contract) => parseContractWithHistory(contract) + ) + + return parsedContracts + } catch (err) { + console.error('PRISMA ERROR', err) + return err + } +} + +export { findAllContractsWithHistoryBySubmittedAt } diff --git a/services/app-api/src/postgres/contractAndRates/index.ts b/services/app-api/src/postgres/contractAndRates/index.ts index f0eefdd9a8..5923fe88ea 100644 --- a/services/app-api/src/postgres/contractAndRates/index.ts +++ b/services/app-api/src/postgres/contractAndRates/index.ts @@ -2,11 +2,13 @@ import type { InsertContractArgsType } from './insertContract' import { insertDraftContract } from './insertContract' import { findContractWithHistory } from './findContractWithHistory' import { findAllContractsWithHistoryByState } from './findAllContractsWithHistoryByState' +import { findAllContractsWithHistoryBySubmittedAt } from './findAllContractsWithHistoryBySubmittedAt' export { insertDraftContract, findContractWithHistory, findAllContractsWithHistoryByState, + findAllContractsWithHistoryBySubmittedAt, } export type { InsertContractArgsType } From dd3e0212c870af0aaef0377e9ddb755a92fe4de9 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 23 Aug 2023 14:11:16 -0400 Subject: [PATCH 04/16] Rename function. --- ...ts => findAllContractsWithHistoryBySubmitInfo.test.ts} | 0 ...edAt.ts => findAllContractsWithHistoryBySubmitInfo.ts} | 8 ++++---- services/app-api/src/postgres/contractAndRates/index.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename services/app-api/src/postgres/contractAndRates/{findAllContractsWithHistoryBySubmittedAt.test.ts => findAllContractsWithHistoryBySubmitInfo.test.ts} (100%) rename services/app-api/src/postgres/contractAndRates/{findAllContractsWithHistoryBySubmittedAt.ts => findAllContractsWithHistoryBySubmitInfo.ts} (87%) diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.test.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts similarity index 100% rename from services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.test.ts rename to services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts similarity index 87% rename from services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.ts rename to services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts index 21ec6af773..f46698363f 100644 --- a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmittedAt.ts +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts @@ -4,7 +4,7 @@ import { NotFoundError } from '../storeError' import { parseContractWithHistory } from './parseContractWithHistory' import { includeFullContract } from './prismaSubmittedContractHelpers' -async function findAllContractsWithHistoryBySubmittedAt( +async function findAllContractsWithHistoryBySubmitInfo( client: PrismaTransactionType, stateCode: string ): Promise | NotFoundError | Error> { @@ -13,8 +13,8 @@ async function findAllContractsWithHistoryBySubmittedAt( where: { revisions: { some: { - submitInfoID: { - not: null, + submitInfo: { + isNot: null, }, }, }, @@ -42,4 +42,4 @@ async function findAllContractsWithHistoryBySubmittedAt( } } -export { findAllContractsWithHistoryBySubmittedAt } +export { findAllContractsWithHistoryBySubmitInfo } diff --git a/services/app-api/src/postgres/contractAndRates/index.ts b/services/app-api/src/postgres/contractAndRates/index.ts index 5923fe88ea..88b235fca4 100644 --- a/services/app-api/src/postgres/contractAndRates/index.ts +++ b/services/app-api/src/postgres/contractAndRates/index.ts @@ -2,13 +2,13 @@ import type { InsertContractArgsType } from './insertContract' import { insertDraftContract } from './insertContract' import { findContractWithHistory } from './findContractWithHistory' import { findAllContractsWithHistoryByState } from './findAllContractsWithHistoryByState' -import { findAllContractsWithHistoryBySubmittedAt } from './findAllContractsWithHistoryBySubmittedAt' +import { findAllContractsWithHistoryBySubmitInfo } from './findAllContractsWithHistoryBySubmitInfo' export { insertDraftContract, findContractWithHistory, findAllContractsWithHistoryByState, - findAllContractsWithHistoryBySubmittedAt, + findAllContractsWithHistoryBySubmitInfo, } export type { InsertContractArgsType } From 421fc8fb6dc6d412f7751c8b3ad7f73196872d77 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Wed, 23 Aug 2023 16:47:46 -0400 Subject: [PATCH 05/16] Test for find contracts by submit info. --- ...llContractsWithHistoryBySubmitInfo.test.ts | 127 +++++++++++++++++- ...findAllContractsWithHistoryBySubmitInfo.ts | 5 +- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts index 04834b8aaf..273d0b8fbd 100644 --- a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts @@ -1,3 +1,128 @@ +import { findAllContractsWithHistoryBySubmitInfo } from './findAllContractsWithHistoryBySubmitInfo' +import { sharedTestPrismaClient } from '../../testHelpers/storeHelpers' +import { createInsertContractData, must } from '../../testHelpers' +import { v4 as uuidv4 } from 'uuid' +import { insertDraftContract } from './insertContract' +import { submitContract } from './submitContract' +import { unlockContract } from './unlockContract' + describe('findAllContractsWithHistoryBySubmittedAt', () => { - it.todo('returns only contracts that have been submitted') + it('returns only contracts that have been submitted or unlocked', async () => { + const client = await sharedTestPrismaClient() + const stateUser = await client.user.create({ + data: { + id: uuidv4(), + givenName: 'Aang', + familyName: 'Avatar', + email: 'aang@example.com', + role: 'STATE_USER', + stateCode: 'NM', + }, + }) + + const cmsUser = await client.user.create({ + data: { + id: uuidv4(), + givenName: 'Zuko', + familyName: 'Hotman', + email: 'zuko@example.com', + role: 'CMS_USER', + }, + }) + + const draftContractData = createInsertContractData({ + submissionDescription: 'one contract', + }) + + // make two submitted contracts and submit them + const contractOne = must( + await insertDraftContract(client, draftContractData) + ) + const contractTwo = must( + await insertDraftContract(client, draftContractData) + ) + const submittedContractOne = must( + await submitContract( + client, + contractOne.id, + stateUser.id, + 'contractOne submit' + ) + ) + const submittedContractTwo = must( + await submitContract( + client, + contractTwo.id, + stateUser.id, + 'contractTwo submit' + ) + ) + + // make two draft contracts + const draftContractOne = must( + await insertDraftContract(client, draftContractData) + ) + const draftContractTwo = must( + await insertDraftContract(client, draftContractData) + ) + + // make one unlocked contract + const contractThree = must( + await insertDraftContract(client, draftContractData) + ) + must( + await submitContract( + client, + contractThree.id, + stateUser.id, + 'unlockContractOne submit' + ) + ) + const unlockedContract = must( + await unlockContract( + client, + contractThree.id, + cmsUser.id, + 'unlock unlockContractOne' + ) + ) + + // call the find by submit info function + const contracts = must( + await findAllContractsWithHistoryBySubmitInfo(client) + ) + + // expect our two submitted contracts + expect(contracts).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: submittedContractOne.id, + }), + expect.objectContaining({ + id: submittedContractTwo.id, + }), + ]) + ) + + // expect our one unlocked contract + expect(contracts).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: unlockedContract.id, + }), + ]) + ) + + // expect our two draft contracts to not be in the results + expect(contracts).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: draftContractOne.id, + }), + expect.objectContaining({ + id: draftContractTwo.id, + }), + ]) + ) + }) }) diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts index f46698363f..8cbc6e742d 100644 --- a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts @@ -5,8 +5,7 @@ import { parseContractWithHistory } from './parseContractWithHistory' import { includeFullContract } from './prismaSubmittedContractHelpers' async function findAllContractsWithHistoryBySubmitInfo( - client: PrismaTransactionType, - stateCode: string + client: PrismaTransactionType ): Promise | NotFoundError | Error> { try { const contracts = await client.contractTable.findMany({ @@ -26,7 +25,7 @@ async function findAllContractsWithHistoryBySubmitInfo( }) if (!contracts) { - const err = `PRISMA ERROR: Cannot find contracts with state code: ${stateCode}` + const err = `PRISMA ERROR: Cannot find all contracts by submit info` console.error(err) return new NotFoundError(err) } From 0933f3d347b40192ea5ff22fceb94713578268c5 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 24 Aug 2023 15:23:28 -0400 Subject: [PATCH 06/16] Update and add find all contracts functions. --- .../findAllContractsWithHistoryByState.ts | 19 +++++++++++++++---- ...findAllContractsWithHistoryBySubmitInfo.ts | 11 +++++++---- .../src/postgres/contractAndRates/index.ts | 3 ++- .../app-api/src/postgres/postgresStore.ts | 14 ++++++++++++-- .../app-api/src/testHelpers/storeHelpers.ts | 5 +++++ 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts index cb3a520f21..807c1325a6 100644 --- a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryByState.ts @@ -4,10 +4,17 @@ import { NotFoundError } from '../storeError' import { parseContractWithHistory } from './parseContractWithHistory' import { includeFullContract } from './prismaSubmittedContractHelpers' +type ContractOrErrorType = { + contractID: string + contract: ContractType | Error +} + +type ContractOrErrorArrayType = ContractOrErrorType[] + async function findAllContractsWithHistoryByState( client: PrismaTransactionType, stateCode: string -): Promise | NotFoundError | Error> { +): Promise { try { const contracts = await client.contractTable.findMany({ where: { @@ -24,11 +31,14 @@ async function findAllContractsWithHistoryByState( return new NotFoundError(err) } - const parsedContracts: Array = contracts.map( - (contract) => parseContractWithHistory(contract) + const parsedContractsOrErrors: ContractOrErrorArrayType = contracts.map( + (contract) => ({ + contractID: contract.id, + contract: parseContractWithHistory(contract), + }) ) - return parsedContracts + return parsedContractsOrErrors } catch (err) { console.error('PRISMA ERROR', err) return err @@ -36,3 +46,4 @@ async function findAllContractsWithHistoryByState( } export { findAllContractsWithHistoryByState } +export type { ContractOrErrorArrayType } diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts index 8cbc6e742d..836f3de513 100644 --- a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.ts @@ -1,12 +1,12 @@ import type { PrismaTransactionType } from '../prismaTypes' -import type { ContractType } from '../../domain-models/contractAndRates' import { NotFoundError } from '../storeError' import { parseContractWithHistory } from './parseContractWithHistory' import { includeFullContract } from './prismaSubmittedContractHelpers' +import type { ContractOrErrorArrayType } from './findAllContractsWithHistoryByState' async function findAllContractsWithHistoryBySubmitInfo( client: PrismaTransactionType -): Promise | NotFoundError | Error> { +): Promise { try { const contracts = await client.contractTable.findMany({ where: { @@ -30,8 +30,11 @@ async function findAllContractsWithHistoryBySubmitInfo( return new NotFoundError(err) } - const parsedContracts: Array = contracts.map( - (contract) => parseContractWithHistory(contract) + const parsedContracts: ContractOrErrorArrayType = contracts.map( + (contract) => ({ + contractID: contract.id, + contract: parseContractWithHistory(contract), + }) ) return parsedContracts diff --git a/services/app-api/src/postgres/contractAndRates/index.ts b/services/app-api/src/postgres/contractAndRates/index.ts index 88b235fca4..cf75d67675 100644 --- a/services/app-api/src/postgres/contractAndRates/index.ts +++ b/services/app-api/src/postgres/contractAndRates/index.ts @@ -1,4 +1,5 @@ import type { InsertContractArgsType } from './insertContract' +import type { ContractOrErrorArrayType } from './findAllContractsWithHistoryByState' import { insertDraftContract } from './insertContract' import { findContractWithHistory } from './findContractWithHistory' import { findAllContractsWithHistoryByState } from './findAllContractsWithHistoryByState' @@ -11,4 +12,4 @@ export { findAllContractsWithHistoryBySubmitInfo, } -export type { InsertContractArgsType } +export type { InsertContractArgsType, ContractOrErrorArrayType } diff --git a/services/app-api/src/postgres/postgresStore.ts b/services/app-api/src/postgres/postgresStore.ts index 090e786f15..c1b2673f73 100644 --- a/services/app-api/src/postgres/postgresStore.ts +++ b/services/app-api/src/postgres/postgresStore.ts @@ -52,8 +52,12 @@ import { insertDraftContract, findContractWithHistory, findAllContractsWithHistoryByState, + findAllContractsWithHistoryBySubmitInfo, +} from './contractAndRates' +import type { + InsertContractArgsType, + ContractOrErrorArrayType, } from './contractAndRates' -import type { InsertContractArgsType } from './contractAndRates' type Store = { findPrograms: ( @@ -141,7 +145,11 @@ type Store = { findAllContractsWithHistoryByState: ( stateCode: string - ) => Promise | Error> + ) => Promise + + findAllContractsWithHistoryBySubmitInfo: () => Promise< + ContractOrErrorArrayType | Error + > } function NewPostgresStore(client: PrismaClient): Store { @@ -204,6 +212,8 @@ function NewPostgresStore(client: PrismaClient): Store { findContractWithHistory(client, args), findAllContractsWithHistoryByState: (args) => findAllContractsWithHistoryByState(client, args), + findAllContractsWithHistoryBySubmitInfo: () => + findAllContractsWithHistoryBySubmitInfo(client), } } diff --git a/services/app-api/src/testHelpers/storeHelpers.ts b/services/app-api/src/testHelpers/storeHelpers.ts index b6534c6232..a469558f28 100644 --- a/services/app-api/src/testHelpers/storeHelpers.ts +++ b/services/app-api/src/testHelpers/storeHelpers.ts @@ -112,6 +112,11 @@ function mockStoreThatErrors(): Store { 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' ) }, + findAllContractsWithHistoryBySubmitInfo: async () => { + return new Error( + 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' + ) + }, } } From d7c3b36e9bdfdc1caf5bc97e7026c029cc6581bb Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 24 Aug 2023 15:55:19 -0400 Subject: [PATCH 07/16] Add find contracts functions to index resolver. --- .../indexHealthPlanPackages.ts | 106 +++++++++++++++++- 1 file changed, 101 insertions(+), 5 deletions(-) diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts index ceed506dfb..f236555532 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts @@ -1,18 +1,26 @@ import type { Span } from '@opentelemetry/api' import { ForbiddenError } from 'apollo-server-lambda' import type { HealthPlanPackageType } from '../../domain-models' -import { isStateUser, isCMSUser, isAdminUser } from '../../domain-models' +import { + isStateUser, + isCMSUser, + isAdminUser, + convertContractToUnlockedHealthPlanPackage, +} from '../../domain-models' import { isHelpdeskUser } from '../../domain-models/user' import type { QueryResolvers } from '../../gen/gqlServer' import { logError, logSuccess } from '../../logger' import type { Store, StoreError } from '../../postgres' -import { isStoreError } from '../../postgres' +import { isStoreError, NotFoundError } from '../../postgres' import { setErrorAttributesOnActiveSpan, setResolverDetailsOnActiveSpan, setSuccessAttributesOnActiveSpan, } from '../attributeHelper' import type { LDService } from '../../launchDarkly/launchDarkly' +import type { ContractOrErrorArrayType } from '../../postgres/contractAndRates' +import type { ContractType } from '../../domain-models/contractAndRates' +import { GraphQLError } from 'graphql/index' const validateAndReturnHealthPlanPackages = ( results: HealthPlanPackageType[] | StoreError, @@ -40,6 +48,56 @@ const validateAndReturnHealthPlanPackages = ( return { totalCount: edges.length, edges } } +const validateContractsAndConvert = ( + parsedContractsOrErrors: ContractOrErrorArrayType, + span?: Span +): HealthPlanPackageType[] => { + // separate valid contracts and errors + const parsedContracts: ContractType[] = [] + const errorParseContracts: string[] = [] + parsedContractsOrErrors.forEach((parsed) => { + if (parsed.contract instanceof Error) { + errorParseContracts.push(parsed.contractID) + } else { + parsedContracts.push(parsed.contract) + } + }) + + // log all contracts that failed parsing to otel. + if (errorParseContracts.length > 0) { + const errMessage = `Failed to parse the following contracts:\n${errorParseContracts.join( + '\n' + )}` + logError('indexHealthPlanPackagesResolver', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + } + + // convert contract type to health plan package type + const convertedContracts: HealthPlanPackageType[] = [] + const errorConvertContracts: string[] = [] + // convert all valid contracts to HPP type + parsedContracts.forEach((contract) => { + const parsedContract = + convertContractToUnlockedHealthPlanPackage(contract) + if (parsedContract instanceof Error) { + errorConvertContracts.push(contract.id) + } else { + convertedContracts.push(parsedContract) + } + }) + + // log all contracts that failed converting + if (errorConvertContracts.length > 0) { + const errMessage = `Failed to covert the following contracts to health plan packages:\n${errorConvertContracts.join( + '\n' + )}` + logError('indexHealthPlanPackagesResolver', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + } + + return convertedContracts +} + export function indexHealthPlanPackagesResolver( store: Store, launchDarkly: LDService @@ -48,10 +106,48 @@ export function indexHealthPlanPackagesResolver( const { user, span } = context setResolverDetailsOnActiveSpan('fetchHealthPlanPackage', user, span) + const ratesDatabaseRefactor = await launchDarkly.getFeatureFlag( + context, + 'rates-db-refactor' + ) + if (isStateUser(user)) { - const results = await store.findAllHealthPlanPackagesByState( - user.stateCode - ) + let results: StoreError | HealthPlanPackageType[] = [] + if (ratesDatabaseRefactor) { + const contractsWithHistory = + await store.findAllContractsWithHistoryByState( + user.stateCode + ) + + if (contractsWithHistory instanceof Error) { + const errMessage = `Issue finding a contract with history by stateCode: ${user.stateCode}. Message: ${contractsWithHistory.message}` + logError('fetchHealthPlanPackage', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + + if (contractsWithHistory instanceof NotFoundError) { + throw new GraphQLError(errMessage, { + extensions: { + code: 'NOT_FOUND', + cause: 'DB_ERROR', + }, + }) + } + + throw new GraphQLError(errMessage, { + extensions: { + code: 'INTERNAL_SERVER_ERROR', + cause: 'DB_ERROR', + }, + }) + } + + results = validateContractsAndConvert(contractsWithHistory) + } else { + results = await store.findAllHealthPlanPackagesByState( + user.stateCode + ) + } + return validateAndReturnHealthPlanPackages(results, span) } else if ( isCMSUser(user) || From e583b5763737b3dc4b50bc244b218ad40dacb27b Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 24 Aug 2023 16:13:38 -0400 Subject: [PATCH 08/16] Add find contracts for CMS, Admin and help desk users. Update error messages. --- .../indexHealthPlanPackages.ts | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts index f236555532..17d87bc48f 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts @@ -49,13 +49,13 @@ const validateAndReturnHealthPlanPackages = ( } const validateContractsAndConvert = ( - parsedContractsOrErrors: ContractOrErrorArrayType, + contractsWithHistory: ContractOrErrorArrayType, span?: Span ): HealthPlanPackageType[] => { // separate valid contracts and errors const parsedContracts: ContractType[] = [] const errorParseContracts: string[] = [] - parsedContractsOrErrors.forEach((parsed) => { + contractsWithHistory.forEach((parsed) => { if (parsed.contract instanceof Error) { errorParseContracts.push(parsed.contractID) } else { @@ -72,10 +72,9 @@ const validateContractsAndConvert = ( setErrorAttributesOnActiveSpan(errMessage, span) } - // convert contract type to health plan package type + // convert contract type to health plan package type and filter out failures const convertedContracts: HealthPlanPackageType[] = [] const errorConvertContracts: string[] = [] - // convert all valid contracts to HPP type parsedContracts.forEach((contract) => { const parsedContract = convertContractToUnlockedHealthPlanPackage(contract) @@ -120,7 +119,7 @@ export function indexHealthPlanPackagesResolver( ) if (contractsWithHistory instanceof Error) { - const errMessage = `Issue finding a contract with history by stateCode: ${user.stateCode}. Message: ${contractsWithHistory.message}` + const errMessage = `Issue finding contracts with history by stateCode: ${user.stateCode}. Message: ${contractsWithHistory.message}` logError('fetchHealthPlanPackage', errMessage) setErrorAttributesOnActiveSpan(errMessage, span) @@ -154,7 +153,37 @@ export function indexHealthPlanPackagesResolver( isAdminUser(user) || isHelpdeskUser(user) ) { - const results = await store.findAllHealthPlanPackagesBySubmittedAt() + let results: StoreError | HealthPlanPackageType[] = [] + if (ratesDatabaseRefactor) { + const contractsWithHistory = + await store.findAllContractsWithHistoryBySubmitInfo() + + if (contractsWithHistory instanceof Error) { + const errMessage = `Issue finding contracts with history by submit info. Message: ${contractsWithHistory.message}` + logError('fetchHealthPlanPackage', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + + if (contractsWithHistory instanceof NotFoundError) { + throw new GraphQLError(errMessage, { + extensions: { + code: 'NOT_FOUND', + cause: 'DB_ERROR', + }, + }) + } + + throw new GraphQLError(errMessage, { + extensions: { + code: 'INTERNAL_SERVER_ERROR', + cause: 'DB_ERROR', + }, + }) + } + + results = validateContractsAndConvert(contractsWithHistory) + } else { + results = await store.findAllHealthPlanPackagesBySubmittedAt() + } return validateAndReturnHealthPlanPackages(results, span) } else { From f1ee9ac00950d037edf22f839228a23f3ebe024f Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 24 Aug 2023 17:24:41 -0400 Subject: [PATCH 09/16] Fix tests from merge. --- .../findAllContractsWithHistoryBySubmitInfo.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts index 273d0b8fbd..6b36b7e611 100644 --- a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts @@ -96,10 +96,10 @@ describe('findAllContractsWithHistoryBySubmittedAt', () => { expect(contracts).toEqual( expect.arrayContaining([ expect.objectContaining({ - id: submittedContractOne.id, + contractID: submittedContractOne.id, }), expect.objectContaining({ - id: submittedContractTwo.id, + contractID: submittedContractTwo.id, }), ]) ) @@ -108,7 +108,7 @@ describe('findAllContractsWithHistoryBySubmittedAt', () => { expect(contracts).toEqual( expect.arrayContaining([ expect.objectContaining({ - id: unlockedContract.id, + contractID: unlockedContract.id, }), ]) ) @@ -117,10 +117,10 @@ describe('findAllContractsWithHistoryBySubmittedAt', () => { expect(contracts).not.toEqual( expect.arrayContaining([ expect.objectContaining({ - id: draftContractOne.id, + contractID: draftContractOne.id, }), expect.objectContaining({ - id: draftContractTwo.id, + contractID: draftContractTwo.id, }), ]) ) From e8ddfdce179e09f450aa1319e8667d1b7402e863 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Thu, 24 Aug 2023 17:25:20 -0400 Subject: [PATCH 10/16] Add todo test for unlock and submit resolver migration. --- .../healthPlanPackage/indexHealthPlanPackages.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts index e55dac4de3..621abcb7da 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts @@ -17,6 +17,9 @@ import { latestFormData } from '../../testHelpers/healthPlanPackageHelpers' import { testCMSUser, testStateUser } from '../../testHelpers/userHelpers' describe('indexHealthPlanPackages', () => { + it.todo( + 'run all tests with rates-db-refactor on after submit and unlock resolvers have been migrated' + ) const cmsUser = testCMSUser() describe('isStateUser', () => { it('returns a list of submissions that includes newly created entries', async () => { From d343e499d999816db4a010b2c9685eea36136196 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Mon, 28 Aug 2023 13:00:16 -0400 Subject: [PATCH 11/16] Test for filtering and logging errors. --- .../indexHealthPlanPackages.test.ts | 116 ++++++++++++++++++ .../indexHealthPlanPackages.ts | 8 +- .../app-api/src/testHelpers/storeHelpers.ts | 24 ++-- 3 files changed, 131 insertions(+), 17 deletions(-) diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts index 621abcb7da..36bae00a52 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts @@ -15,6 +15,14 @@ import type { } from '../../gen/gqlServer' import { latestFormData } from '../../testHelpers/healthPlanPackageHelpers' import { testCMSUser, testStateUser } from '../../testHelpers/userHelpers' +import { NewPostgresStore } from '../../postgres' +import type { NotFoundError, Store } from '../../postgres' +import { sharedTestPrismaClient } from '../../testHelpers/storeHelpers' +import type { PrismaTransactionType } from '../../postgres/prismaTypes' +import type { ContractOrErrorArrayType } from '../../postgres/contractAndRates' +import { createContractData, createDraftContractData } from '../../testHelpers' +import { parseContractWithHistory } from '../../postgres/contractAndRates/parseContractWithHistory' +import { testLDService } from '../../testHelpers/launchDarklyHelpers' describe('indexHealthPlanPackages', () => { it.todo( @@ -370,3 +378,111 @@ describe('indexHealthPlanPackages', () => { }) }) }) +describe('indexHealthPlanPackages test rates-db-refactor flag on only', () => { + afterEach(() => { + jest.restoreAllMocks() + }) + + it('correctly filters and log contracts that failed parsing', async () => { + const mockFeatureFlag = testLDService({ 'rates-db-refactor': true }) + const client = await sharedTestPrismaClient() + const errors = jest.spyOn(global.console, 'error').mockImplementation() + + const stateUser = testStateUser() + + // Valid draft contract + const validParsedDraftContract = parseContractWithHistory( + createDraftContractData({ + stateCode: stateUser.stateCode, + }) + ) + + if (validParsedDraftContract instanceof Error) { + throw new Error('Unexpected error in parsing contract in test') + } + + // Valid submitted contract that will error because we cannot convert submitted contract yet, after we implement + // the function to convert submitted contracts we need to figure out how to get this to error, or remove testing + // conversion errors. + const validParsedSubmittedContract = parseContractWithHistory( + createContractData({ + stateCode: stateUser.stateCode, + }) + ) + + if (validParsedSubmittedContract instanceof Error) { + throw new Error('Unexpected error in parsing contract in test') + } + + // create find all that returns parsed contracts and errors + const findAllContractsWithHistoryByState = ( + client: PrismaTransactionType, + stateCode: string + ): Promise => { + const contracts: ContractOrErrorArrayType = [ + { + contractID: validParsedDraftContract.id, + contract: validParsedDraftContract, + }, + { + contractID: validParsedSubmittedContract.id, + contract: validParsedSubmittedContract, + }, + { + contractID: 'errorParsingContract', + contract: new Error('Parsing Error'), + }, + ] + return new Promise((resolve) => resolve(contracts)) + } + + const defaultStore = await NewPostgresStore(client) + const mockStore: Store = { + ...defaultStore, + findAllContractsWithHistoryByState: (args) => + findAllContractsWithHistoryByState(client, stateUser.stateCode), + } + + const server = await constructTestPostgresServer({ + store: mockStore, + ldService: mockFeatureFlag, + context: { + user: stateUser, + }, + }) + + // get all submissions by state + const result = await server.executeOperation({ + query: INDEX_HEALTH_PLAN_PACKAGES, + }) + + const contracts = result.data?.indexHealthPlanPackages.edges + + // expect console.error to log contract that failed parsing + expect(errors).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'indexHealthPlanPackagesResolver failed', + error: expect.stringContaining('errorParsingContract'), + }) + ) + + // expect console.error to log contract that failed coverting + expect(errors).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'indexHealthPlanPackagesResolver failed', + error: expect.stringContaining(validParsedSubmittedContract.id), + }) + ) + + // Expect our contract that passed checks + expect(contracts).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + node: expect.objectContaining({ + id: validParsedDraftContract.id, + }), + }), + ]) + ) + }) +}) diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts index 17d87bc48f..0189dbc9b3 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts @@ -57,7 +57,9 @@ const validateContractsAndConvert = ( const errorParseContracts: string[] = [] contractsWithHistory.forEach((parsed) => { if (parsed.contract instanceof Error) { - errorParseContracts.push(parsed.contractID) + errorParseContracts.push( + `${parsed.contractID}: ${parsed.contract.message}` + ) } else { parsedContracts.push(parsed.contract) } @@ -79,7 +81,9 @@ const validateContractsAndConvert = ( const parsedContract = convertContractToUnlockedHealthPlanPackage(contract) if (parsedContract instanceof Error) { - errorConvertContracts.push(contract.id) + errorConvertContracts.push( + `${contract.id}: ${parsedContract.message}` + ) } else { convertedContracts.push(parsedContract) } diff --git a/services/app-api/src/testHelpers/storeHelpers.ts b/services/app-api/src/testHelpers/storeHelpers.ts index 29a6d873f5..34a8d8f625 100644 --- a/services/app-api/src/testHelpers/storeHelpers.ts +++ b/services/app-api/src/testHelpers/storeHelpers.ts @@ -38,6 +38,10 @@ function mockStoreThatErrors(): Store { message: 'this error came from the generic store with errors mock', } + const genericError: Error = new Error( + 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' + ) + return { findAllHealthPlanPackagesByState: async (_stateCode) => { return genericStoreError @@ -98,30 +102,20 @@ function mockStoreThatErrors(): Store { return genericStoreError }, insertDraftContract: async (_ID) => { - return new Error( - 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' - ) + return genericError }, findContractWithHistory: async (_ID) => { - return new Error( - 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' - ) + return genericError }, updateDraftContract: async (_ID) => { - return new Error( - 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' - ) + return genericError }, findAllContractsWithHistoryByState: async (_ID) => { - return new Error( - 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' - ) + return genericError }, findAllContractsWithHistoryBySubmitInfo: async () => { - return new Error( - 'UNEXPECTED_EXCEPTION: This error came from the generic store with errors mock' - ) + return genericError }, } } From 2ee304539ba72dc55ec7ed44c9eacb51a884fa39 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Mon, 28 Aug 2023 13:10:27 -0400 Subject: [PATCH 12/16] Fix test name. --- .../findAllContractsWithHistoryBySubmitInfo.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts index 6b36b7e611..57971c69b2 100644 --- a/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts +++ b/services/app-api/src/postgres/contractAndRates/findAllContractsWithHistoryBySubmitInfo.test.ts @@ -6,7 +6,7 @@ import { insertDraftContract } from './insertContract' import { submitContract } from './submitContract' import { unlockContract } from './unlockContract' -describe('findAllContractsWithHistoryBySubmittedAt', () => { +describe('findAllContractsWithHistoryBySubmittedInfo', () => { it('returns only contracts that have been submitted or unlocked', async () => { const client = await sharedTestPrismaClient() const stateUser = await client.user.create({ From 1992b1b1b944b458ceb8a76d40ec541f8fd2ae9e Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Mon, 28 Aug 2023 14:06:32 -0400 Subject: [PATCH 13/16] Fix programs in test data. --- .../src/domain-models/healthPlanPackage.ts | 2 - .../findRateWithHistory.test.ts | 10 +-- .../contractAndRates/insertContract.test.ts | 3 +- .../contractAndRates/submitContract.test.ts | 4 +- .../contractAndRates/contractHelpers.ts | 48 ++++++++---- .../contractAndRates/rateHelpers.ts | 73 ++++++++----------- services/app-api/src/testHelpers/index.ts | 1 + 7 files changed, 73 insertions(+), 68 deletions(-) diff --git a/services/app-api/src/domain-models/healthPlanPackage.ts b/services/app-api/src/domain-models/healthPlanPackage.ts index 4bf659cf01..0ce789d595 100644 --- a/services/app-api/src/domain-models/healthPlanPackage.ts +++ b/services/app-api/src/domain-models/healthPlanPackage.ts @@ -72,8 +72,6 @@ function packageSubmitters(pkg: HealthPlanPackageType): string[] { function convertContractToUnlockedHealthPlanPackage( contract: ContractType ): HealthPlanPackageType | Error { - console.info('Attempting to convert contract to health plan package') - // Since drafts come in separate on the Contract type, we push it onto the revisions before converting below if (contract.draftRevision) { contract.revisions.unshift(contract.draftRevision) diff --git a/services/app-api/src/postgres/contractAndRates/findRateWithHistory.test.ts b/services/app-api/src/postgres/contractAndRates/findRateWithHistory.test.ts index 90324f1371..5d8fc6a5a2 100644 --- a/services/app-api/src/postgres/contractAndRates/findRateWithHistory.test.ts +++ b/services/app-api/src/postgres/contractAndRates/findRateWithHistory.test.ts @@ -459,7 +459,7 @@ describe('findRate', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'a.1 body', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }, @@ -491,7 +491,7 @@ describe('findRate', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'a.2 body', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }, @@ -532,7 +532,7 @@ describe('findRate', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'one contract', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }) @@ -549,7 +549,7 @@ describe('findRate', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'one contract', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }) @@ -567,7 +567,7 @@ describe('findRate', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'a.1 body', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }) diff --git a/services/app-api/src/postgres/contractAndRates/insertContract.test.ts b/services/app-api/src/postgres/contractAndRates/insertContract.test.ts index 9eb300bbdf..2bb8252293 100644 --- a/services/app-api/src/postgres/contractAndRates/insertContract.test.ts +++ b/services/app-api/src/postgres/contractAndRates/insertContract.test.ts @@ -40,7 +40,7 @@ describe('insertContract', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'Contract 1.0', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }), @@ -83,6 +83,7 @@ describe('insertContract', () => { const draftContractData = createInsertContractData({ stateCode: 'CANADA' as StateCodeType, + programIDs: [], }) const draftContract = await insertDraftContract( client, diff --git a/services/app-api/src/postgres/contractAndRates/submitContract.test.ts b/services/app-api/src/postgres/contractAndRates/submitContract.test.ts index 30f731a0a1..084df63290 100644 --- a/services/app-api/src/postgres/contractAndRates/submitContract.test.ts +++ b/services/app-api/src/postgres/contractAndRates/submitContract.test.ts @@ -59,7 +59,7 @@ describe('submitContract', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'one contract', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }), @@ -149,7 +149,7 @@ describe('submitContract', () => { submissionType: 'CONTRACT_AND_RATES', submissionDescription: 'second contract revision', contractType: 'BASE', - programIDs: ['PMAP'], + programIDs: draftContractData.programIDs, populationCovered: 'MEDICAID', riskBasedContract: false, }, diff --git a/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts b/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts index ea8d53ad24..2bd7e37d8c 100644 --- a/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts +++ b/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts @@ -9,20 +9,30 @@ import type { } from '../../postgres/contractAndRates/prismaSubmittedContractHelpers' import type { StateCodeType } from 'app-web/src/common-code/healthPlanFormDataType' import type { ContractFormDataType } from '../../domain-models/contractAndRates' +import type { ProgramType } from '../../domain-models' +import statePrograms from 'app-web/src/common-code/data/statePrograms.json' + +function getProgramsFromState(stateCode: StateCodeType): ProgramType[] { + const state = statePrograms.states.find((st) => st.code === stateCode) + + return state?.programs || [] +} const createInsertContractData = ({ - stateCode, + stateCode = 'MN', ...formData }: { stateCode?: StateCodeType } & Partial): InsertContractArgsType => { return { - stateCode: stateCode ?? 'MN', + stateCode: stateCode, submissionType: formData?.submissionType ?? 'CONTRACT_AND_RATES', submissionDescription: formData?.submissionDescription ?? 'Contract 1.0', contractType: formData?.contractType ?? 'BASE', - programIDs: formData?.programIDs ?? ['PMAP'], + programIDs: formData?.programIDs ?? [ + getProgramsFromState(stateCode ?? 'MN')[0].id, + ], populationCovered: formData?.populationCovered ?? 'MEDICAID', riskBasedContract: formData?.riskBasedContract ?? false, } @@ -53,13 +63,16 @@ const createDraftContractData = ( id: '24fb2a5f-6d0d-4e26-9906-4de28927c882', createdAt: new Date(), updatedAt: new Date(), - stateCode: 'FL', + stateCode: 'MN', stateNumber: 111, revisions: contract?.revisions ?? [ - createContractRevision({ - rateRevisions: undefined, - submitInfo: null, - }) as ContractRevisionTableWithRates, + createContractRevision( + { + rateRevisions: undefined, + submitInfo: null, + }, + contract?.stateCode as StateCodeType + ) as ContractRevisionTableWithRates, ], ...contract, }) @@ -70,18 +83,22 @@ const createContractData = ( id: '24fb2a5f-6d0d-4e26-9906-4de28927c882', createdAt: new Date(), updatedAt: new Date(), - stateCode: 'FL', + stateCode: 'MN', stateNumber: 111, revisions: contract?.revisions ?? [ - createContractRevision({ - draftRates: undefined, - }) as ContractRevisionTableWithRates, + createContractRevision( + { + draftRates: undefined, + }, + contract?.stateCode as StateCodeType + ) as ContractRevisionTableWithRates, ], ...contract, }) const createContractRevision = ( - revision?: Partial + revision?: Partial, + stateCode: StateCodeType = 'MN' ): ContractRevisionTableWithRates => ({ id: uuidv4(), createdAt: new Date(), @@ -100,14 +117,14 @@ const createContractRevision = ( email: 'boblaw@example.com', role: 'STATE_USER', divisionAssignment: null, - stateCode: 'OH', + stateCode: stateCode, }, }, unlockInfo: null, contractID: 'contractID', submitInfoID: null, unlockInfoID: null, - programIDs: ['Program'], + programIDs: [getProgramsFromState(stateCode)[0].id], populationCovered: 'MEDICAID' as const, submissionType: 'CONTRACT_ONLY' as const, riskBasedContract: false, @@ -178,4 +195,5 @@ export { createContractRevision, createContractData, createDraftContractData, + getProgramsFromState, } diff --git a/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts b/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts index db6faa98dc..e28d0d315b 100644 --- a/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts +++ b/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts @@ -1,50 +1,37 @@ - -import { must } from '../errorHelpers' import { v4 as uuidv4 } from 'uuid' -import type { PrismaClient,State } from '@prisma/client' import type { InsertRateArgsType } from '../../postgres/contractAndRates/insertRate' -import type { RateTableFullPayload, RateRevisionTableWithContracts } from '../../postgres/contractAndRates/prismaSubmittedRateHelpers' +import type { + RateTableFullPayload, + RateRevisionTableWithContracts, +} from '../../postgres/contractAndRates/prismaSubmittedRateHelpers' +import { getProgramsFromState } from './contractHelpers' +import type { StateCodeType } from 'app-web/src/common-code/healthPlanFormDataType' + const createInsertRateData = ( rateArgs?: Partial ): InsertRateArgsType => { return { stateCode: rateArgs?.stateCode ?? 'MN', - ...rateArgs + ...rateArgs, } } -const getStateRecord = async ( - client: PrismaClient, - stateCode: string -): Promise => { - const state = must( - await client.state.findFirst({ - where: { - stateCode, - }, - }) - ) - - if (!state) { - throw new Error('Unexpected prisma error: state record not found') - } - - return state -} - const createDraftRateData = ( rate?: Partial -): RateTableFullPayload=> ({ +): RateTableFullPayload => ({ id: '24fb2a5f-6d0d-4e26-9906-4de28927c882', createdAt: new Date(), updatedAt: new Date(), - stateCode: 'FL', + stateCode: 'MN', stateNumber: 111, revisions: rate?.revisions ?? [ - createRateRevision({ - contractRevisions: undefined, - submitInfo: null, - }) as RateRevisionTableWithContracts, + createRateRevision( + { + contractRevisions: undefined, + submitInfo: null, + }, + rate?.stateCode as StateCodeType + ) as RateRevisionTableWithContracts, ], ...rate, }) @@ -55,22 +42,23 @@ const createRateData = ( id: '24fb2a5f-6d0d-4e26-9906-4de28927c882', createdAt: new Date(), updatedAt: new Date(), - stateCode: 'FL', + stateCode: 'MN', stateNumber: 111, revisions: rate?.revisions ?? [ - createRateRevision({ - draftContracts: undefined, - }) as RateRevisionTableWithContracts, + createRateRevision( + { + draftContracts: undefined, + }, + rate?.stateCode as StateCodeType + ) as RateRevisionTableWithContracts, ], ...rate, }) const createRateRevision = ( - revision?: Partial< - RateRevisionTableWithContracts - > -): - RateRevisionTableWithContracts => ({ + revision?: Partial, + stateCode: StateCodeType = 'MN' +): RateRevisionTableWithContracts => ({ id: uuidv4(), createdAt: new Date(), updatedAt: new Date(), @@ -88,16 +76,16 @@ const createRateRevision = ( email: 'boblaw@example.com', role: 'STATE_USER', divisionAssignment: null, - stateCode: 'OH', + stateCode: stateCode, }, }, unlockInfo: null, submitInfoID: null, unlockInfoID: null, rateType: 'NEW', - rateID: 'rateID', + rateID: 'rateID', rateCertificationName: 'testState-123', - rateProgramIDs: ['Program'], + rateProgramIDs: [getProgramsFromState(stateCode)[0].id], rateCapitationType: 'RATE_CELL', rateDateStart: new Date(), rateDateEnd: new Date(), @@ -145,7 +133,6 @@ const createRateRevision = ( export { createInsertRateData, - getStateRecord, createRateRevision, createRateData, createDraftRateData, diff --git a/services/app-api/src/testHelpers/index.ts b/services/app-api/src/testHelpers/index.ts index 31ff891c6e..83bb67c633 100644 --- a/services/app-api/src/testHelpers/index.ts +++ b/services/app-api/src/testHelpers/index.ts @@ -12,4 +12,5 @@ export { createContractData, createContractRevision, createDraftContractData, + getProgramsFromState, } from './contractAndRates/contractHelpers' From 5a522650d5d8344aa2d7a533c2f99b9b90894f06 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Mon, 28 Aug 2023 14:10:51 -0400 Subject: [PATCH 14/16] Update test name. --- .../resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts index 36bae00a52..f3b5306b63 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.test.ts @@ -383,7 +383,7 @@ describe('indexHealthPlanPackages test rates-db-refactor flag on only', () => { jest.restoreAllMocks() }) - it('correctly filters and log contracts that failed parsing', async () => { + it('correctly filters and log contracts that failed parsing or converting', async () => { const mockFeatureFlag = testLDService({ 'rates-db-refactor': true }) const client = await sharedTestPrismaClient() const errors = jest.spyOn(global.console, 'error').mockImplementation() From d261287fc8ce8ac8ef8fef8b7b825d3594310653 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Tue, 29 Aug 2023 14:35:59 -0400 Subject: [PATCH 15/16] Move state helpers to stateHelpers.ts --- .../contractAndRates/contractHelpers.ts | 33 +------------------ .../contractAndRates/rateHelpers.ts | 2 +- .../app-api/src/testHelpers/gqlHelpers.ts | 8 +---- services/app-api/src/testHelpers/index.ts | 4 +-- .../app-api/src/testHelpers/stateHelpers.ts | 32 ++++++++++++++++++ 5 files changed, 37 insertions(+), 42 deletions(-) create mode 100644 services/app-api/src/testHelpers/stateHelpers.ts diff --git a/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts b/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts index 2bd7e37d8c..3464c0c8e0 100644 --- a/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts +++ b/services/app-api/src/testHelpers/contractAndRates/contractHelpers.ts @@ -1,7 +1,4 @@ import type { InsertContractArgsType } from '../../postgres/contractAndRates/insertContract' -import type { State } from '@prisma/client' -import { must } from '../errorHelpers' -import type { PrismaClient } from '@prisma/client' import { v4 as uuidv4 } from 'uuid' import type { ContractRevisionTableWithRates, @@ -9,14 +6,7 @@ import type { } from '../../postgres/contractAndRates/prismaSubmittedContractHelpers' import type { StateCodeType } from 'app-web/src/common-code/healthPlanFormDataType' import type { ContractFormDataType } from '../../domain-models/contractAndRates' -import type { ProgramType } from '../../domain-models' -import statePrograms from 'app-web/src/common-code/data/statePrograms.json' - -function getProgramsFromState(stateCode: StateCodeType): ProgramType[] { - const state = statePrograms.states.find((st) => st.code === stateCode) - - return state?.programs || [] -} +import { getProgramsFromState } from '../stateHelpers' const createInsertContractData = ({ stateCode = 'MN', @@ -38,25 +28,6 @@ const createInsertContractData = ({ } } -const getStateRecord = async ( - client: PrismaClient, - stateCode: string -): Promise => { - const state = must( - await client.state.findFirst({ - where: { - stateCode, - }, - }) - ) - - if (!state) { - throw new Error('Unexpected prisma error: state record not found') - } - - return state -} - const createDraftContractData = ( contract?: Partial ): ContractTableFullPayload => ({ @@ -191,9 +162,7 @@ const createContractRevision = ( export { createInsertContractData, - getStateRecord, createContractRevision, createContractData, createDraftContractData, - getProgramsFromState, } diff --git a/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts b/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts index e28d0d315b..067a88dc1b 100644 --- a/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts +++ b/services/app-api/src/testHelpers/contractAndRates/rateHelpers.ts @@ -4,7 +4,7 @@ import type { RateTableFullPayload, RateRevisionTableWithContracts, } from '../../postgres/contractAndRates/prismaSubmittedRateHelpers' -import { getProgramsFromState } from './contractHelpers' +import { getProgramsFromState } from '../stateHelpers' import type { StateCodeType } from 'app-web/src/common-code/healthPlanFormDataType' const createInsertRateData = ( diff --git a/services/app-api/src/testHelpers/gqlHelpers.ts b/services/app-api/src/testHelpers/gqlHelpers.ts index a717b43712..3badabe096 100644 --- a/services/app-api/src/testHelpers/gqlHelpers.ts +++ b/services/app-api/src/testHelpers/gqlHelpers.ts @@ -36,11 +36,11 @@ import { sharedTestPrismaClient } from './storeHelpers' import { domainToBase64 } from 'app-web/src/common-code/proto/healthPlanFormDataProto' import type { EmailParameterStore } from '../parameterStore' import { newLocalEmailParameterStore } from '../parameterStore' -import statePrograms from 'app-web/src/common-code/data/statePrograms.json' import { testLDService } from './launchDarklyHelpers' import type { LDService } from '../launchDarkly/launchDarkly' import { insertUserToLocalAurora } from '../authn' import { testStateUser } from './userHelpers' +import { getProgramsFromState } from './stateHelpers' // Since our programs are checked into source code, we have a program we // use as our default @@ -60,12 +60,6 @@ function defaultFloridaRateProgram(): ProgramType { } } -function getProgramsFromState(stateCode: StateCodeType): ProgramType[] { - const state = statePrograms.states.find((st) => st.code === stateCode) - - return state?.programs || [] -} - const defaultContext = (): Context => { return { user: testStateUser(), diff --git a/services/app-api/src/testHelpers/index.ts b/services/app-api/src/testHelpers/index.ts index 83bb67c633..1ed448dc40 100644 --- a/services/app-api/src/testHelpers/index.ts +++ b/services/app-api/src/testHelpers/index.ts @@ -8,9 +8,9 @@ export { must } from './errorHelpers' export { createInsertContractData, - getStateRecord, createContractData, createContractRevision, createDraftContractData, - getProgramsFromState, } from './contractAndRates/contractHelpers' + +export { getProgramsFromState, getStateRecord } from './stateHelpers' diff --git a/services/app-api/src/testHelpers/stateHelpers.ts b/services/app-api/src/testHelpers/stateHelpers.ts new file mode 100644 index 0000000000..cec1ba1d30 --- /dev/null +++ b/services/app-api/src/testHelpers/stateHelpers.ts @@ -0,0 +1,32 @@ +import type { StateCodeType } from 'app-web/src/common-code/healthPlanFormDataType' +import type { ProgramType } from '../domain-models' +import statePrograms from 'app-web/src/common-code/data/statePrograms.json' +import type { PrismaClient, State } from '@prisma/client' +import { must } from './errorHelpers' + +function getProgramsFromState(stateCode: StateCodeType): ProgramType[] { + const state = statePrograms.states.find((st) => st.code === stateCode) + + return state?.programs || [] +} + +async function getStateRecord( + client: PrismaClient, + stateCode: string +): Promise { + const state = must( + await client.state.findFirst({ + where: { + stateCode, + }, + }) + ) + + if (!state) { + throw new Error('Unexpected prisma error: state record not found') + } + + return state +} + +export { getProgramsFromState, getStateRecord } From c80cb6e8e7459c6bb57b9ba3f0c6f3de13550131 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Tue, 29 Aug 2023 15:44:22 -0400 Subject: [PATCH 16/16] Move validateContractsAndConvert to helper file. --- .../contractAndRates/resolverHelpers.ts | 62 ++++++++++++++++++ .../indexHealthPlanPackages.ts | 63 +------------------ 2 files changed, 64 insertions(+), 61 deletions(-) create mode 100644 services/app-api/src/resolvers/healthPlanPackage/contractAndRates/resolverHelpers.ts diff --git a/services/app-api/src/resolvers/healthPlanPackage/contractAndRates/resolverHelpers.ts b/services/app-api/src/resolvers/healthPlanPackage/contractAndRates/resolverHelpers.ts new file mode 100644 index 0000000000..bcf66f0bda --- /dev/null +++ b/services/app-api/src/resolvers/healthPlanPackage/contractAndRates/resolverHelpers.ts @@ -0,0 +1,62 @@ +import type { ContractOrErrorArrayType } from '../../../postgres/contractAndRates' +import type { Span } from '@opentelemetry/api' +import type { HealthPlanPackageType } from '../../../domain-models' +import type { ContractType } from '../../../domain-models/contractAndRates' +import { convertContractToUnlockedHealthPlanPackage } from '../../../domain-models' +import { logError } from '../../../logger' +import { setErrorAttributesOnActiveSpan } from '../../attributeHelper' + +const validateContractsAndConvert = ( + contractsWithHistory: ContractOrErrorArrayType, + span?: Span +): HealthPlanPackageType[] => { + // separate valid contracts and errors + const parsedContracts: ContractType[] = [] + const errorParseContracts: string[] = [] + contractsWithHistory.forEach((parsed) => { + if (parsed.contract instanceof Error) { + errorParseContracts.push( + `${parsed.contractID}: ${parsed.contract.message}` + ) + } else { + parsedContracts.push(parsed.contract) + } + }) + + // log all contracts that failed parsing to otel. + if (errorParseContracts.length > 0) { + const errMessage = `Failed to parse the following contracts:\n${errorParseContracts.join( + '\n' + )}` + logError('indexHealthPlanPackagesResolver', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + } + + // convert contract type to health plan package type and filter out failures + const convertedContracts: HealthPlanPackageType[] = [] + const errorConvertContracts: string[] = [] + parsedContracts.forEach((contract) => { + const parsedContract = + convertContractToUnlockedHealthPlanPackage(contract) + if (parsedContract instanceof Error) { + errorConvertContracts.push( + `${contract.id}: ${parsedContract.message}` + ) + } else { + convertedContracts.push(parsedContract) + } + }) + + // log all contracts that failed converting + if (errorConvertContracts.length > 0) { + const errMessage = `Failed to covert the following contracts to health plan packages:\n${errorConvertContracts.join( + '\n' + )}` + logError('indexHealthPlanPackagesResolver', errMessage) + setErrorAttributesOnActiveSpan(errMessage, span) + } + + return convertedContracts +} + +export { validateContractsAndConvert } diff --git a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts index 0189dbc9b3..f2d8dcad9e 100644 --- a/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts +++ b/services/app-api/src/resolvers/healthPlanPackage/indexHealthPlanPackages.ts @@ -1,12 +1,7 @@ import type { Span } from '@opentelemetry/api' import { ForbiddenError } from 'apollo-server-lambda' import type { HealthPlanPackageType } from '../../domain-models' -import { - isStateUser, - isCMSUser, - isAdminUser, - convertContractToUnlockedHealthPlanPackage, -} from '../../domain-models' +import { isStateUser, isCMSUser, isAdminUser } from '../../domain-models' import { isHelpdeskUser } from '../../domain-models/user' import type { QueryResolvers } from '../../gen/gqlServer' import { logError, logSuccess } from '../../logger' @@ -18,9 +13,8 @@ import { setSuccessAttributesOnActiveSpan, } from '../attributeHelper' import type { LDService } from '../../launchDarkly/launchDarkly' -import type { ContractOrErrorArrayType } from '../../postgres/contractAndRates' -import type { ContractType } from '../../domain-models/contractAndRates' import { GraphQLError } from 'graphql/index' +import { validateContractsAndConvert } from './contractAndRates/resolverHelpers' const validateAndReturnHealthPlanPackages = ( results: HealthPlanPackageType[] | StoreError, @@ -48,59 +42,6 @@ const validateAndReturnHealthPlanPackages = ( return { totalCount: edges.length, edges } } -const validateContractsAndConvert = ( - contractsWithHistory: ContractOrErrorArrayType, - span?: Span -): HealthPlanPackageType[] => { - // separate valid contracts and errors - const parsedContracts: ContractType[] = [] - const errorParseContracts: string[] = [] - contractsWithHistory.forEach((parsed) => { - if (parsed.contract instanceof Error) { - errorParseContracts.push( - `${parsed.contractID}: ${parsed.contract.message}` - ) - } else { - parsedContracts.push(parsed.contract) - } - }) - - // log all contracts that failed parsing to otel. - if (errorParseContracts.length > 0) { - const errMessage = `Failed to parse the following contracts:\n${errorParseContracts.join( - '\n' - )}` - logError('indexHealthPlanPackagesResolver', errMessage) - setErrorAttributesOnActiveSpan(errMessage, span) - } - - // convert contract type to health plan package type and filter out failures - const convertedContracts: HealthPlanPackageType[] = [] - const errorConvertContracts: string[] = [] - parsedContracts.forEach((contract) => { - const parsedContract = - convertContractToUnlockedHealthPlanPackage(contract) - if (parsedContract instanceof Error) { - errorConvertContracts.push( - `${contract.id}: ${parsedContract.message}` - ) - } else { - convertedContracts.push(parsedContract) - } - }) - - // log all contracts that failed converting - if (errorConvertContracts.length > 0) { - const errMessage = `Failed to covert the following contracts to health plan packages:\n${errorConvertContracts.join( - '\n' - )}` - logError('indexHealthPlanPackagesResolver', errMessage) - setErrorAttributesOnActiveSpan(errMessage, span) - } - - return convertedContracts -} - export function indexHealthPlanPackagesResolver( store: Store, launchDarkly: LDService