From af20cd49186b374e66c58bbe92cda58e64135fa6 Mon Sep 17 00:00:00 2001 From: pearl-truss Date: Tue, 21 Jan 2025 09:08:34 -0500 Subject: [PATCH 1/5] add round number to Question object --- .../app-api/src/domain-models/QuestionsType.ts | 1 + .../questionResponse/questionHelpers.ts | 17 ++++++++++++----- .../src/fragments/questionsEdgeFragment.graphql | 1 + services/app-graphql/src/schema.graphql | 1 + 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/services/app-api/src/domain-models/QuestionsType.ts b/services/app-api/src/domain-models/QuestionsType.ts index be1466af72..26c0df975d 100644 --- a/services/app-api/src/domain-models/QuestionsType.ts +++ b/services/app-api/src/domain-models/QuestionsType.ts @@ -16,6 +16,7 @@ const commonQuestionSchema = z.object({ division: divisionType, // DMCO, DMCP, OACT documents: z.array(document), responses: z.array(questionResponseType), + round: z.number(), }) const contractQuestion = commonQuestionSchema.extend({ diff --git a/services/app-api/src/postgres/questionResponse/questionHelpers.ts b/services/app-api/src/postgres/questionResponse/questionHelpers.ts index 9f74ce4ed2..2ce433a360 100644 --- a/services/app-api/src/postgres/questionResponse/questionHelpers.ts +++ b/services/app-api/src/postgres/questionResponse/questionHelpers.ts @@ -67,12 +67,19 @@ const convertToCommonIndexQuestionsPayload = < const getDivisionQuestionsEdge = ( division: 'DMCP' | 'DMCO' | 'OACT', questions: P[] - ) => ({ - totalCount: questions.filter((q) => q.division === division).length, - edges: questions + ) => { + const divisionQuestions = questions .filter((q) => q.division === division) - .map((question) => ({ node: question })), - }) + .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) + .map((question, idx) => { + question.round = idx + 1 + return { node: question } + }) + return { + totalCount: divisionQuestions.length, + edges: divisionQuestions, + } + } return { DMCOQuestions: getDivisionQuestionsEdge('DMCO', questions), diff --git a/services/app-graphql/src/fragments/questionsEdgeFragment.graphql b/services/app-graphql/src/fragments/questionsEdgeFragment.graphql index 28a06b9b6c..84db054cde 100644 --- a/services/app-graphql/src/fragments/questionsEdgeFragment.graphql +++ b/services/app-graphql/src/fragments/questionsEdgeFragment.graphql @@ -12,6 +12,7 @@ fragment contractQuestionEdgeFragment on ContractQuestionEdge { } } division + round documents { s3URL name diff --git a/services/app-graphql/src/schema.graphql b/services/app-graphql/src/schema.graphql index 44c060e0b3..f3f1d42654 100644 --- a/services/app-graphql/src/schema.graphql +++ b/services/app-graphql/src/schema.graphql @@ -1239,6 +1239,7 @@ type ContractQuestion { addedBy: CMSUsersUnion! documents: [Document!]! division: Division! + round: Int! # noteText: String # dueDate: Date # rateIDs: [String!] From fa5593d5bc809168cd0cde4746f2ce2c4771c00e Mon Sep 17 00:00:00 2001 From: pearl-truss Date: Tue, 21 Jan 2025 17:08:45 -0500 Subject: [PATCH 2/5] move add round number logic to resolver chain --- .../questionResponse/questionHelpers.ts | 17 ++--- .../src/resolvers/configureResolvers.ts | 2 + .../src/resolvers/questionResponse/index.ts | 1 + .../questionResponse/questionResolver.ts | 76 +++++++++++++++++++ 4 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 services/app-api/src/resolvers/questionResponse/questionResolver.ts diff --git a/services/app-api/src/postgres/questionResponse/questionHelpers.ts b/services/app-api/src/postgres/questionResponse/questionHelpers.ts index 2ce433a360..9f74ce4ed2 100644 --- a/services/app-api/src/postgres/questionResponse/questionHelpers.ts +++ b/services/app-api/src/postgres/questionResponse/questionHelpers.ts @@ -67,19 +67,12 @@ const convertToCommonIndexQuestionsPayload = < const getDivisionQuestionsEdge = ( division: 'DMCP' | 'DMCO' | 'OACT', questions: P[] - ) => { - const divisionQuestions = questions + ) => ({ + totalCount: questions.filter((q) => q.division === division).length, + edges: questions .filter((q) => q.division === division) - .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) - .map((question, idx) => { - question.round = idx + 1 - return { node: question } - }) - return { - totalCount: divisionQuestions.length, - edges: divisionQuestions, - } - } + .map((question) => ({ node: question })), + }) return { DMCOQuestions: getDivisionQuestionsEdge('DMCO', questions), diff --git a/services/app-api/src/resolvers/configureResolvers.ts b/services/app-api/src/resolvers/configureResolvers.ts index 4c13ad653c..265603dfe4 100644 --- a/services/app-api/src/resolvers/configureResolvers.ts +++ b/services/app-api/src/resolvers/configureResolvers.ts @@ -17,6 +17,7 @@ import { questionResponseDocumentResolver, createRateQuestionResolver, createRateQuestionResponseResolver, + questionResolver, } from './questionResponse' import { fetchCurrentUserResolver, @@ -171,6 +172,7 @@ export function configureResolvers( Rate: rateResolver(store, applicationEndpoint), RateRevision: rateRevisionResolver(store), RateFormData: rateFormDataResolver(), + ContractQuestion: questionResolver(store), Contract: contractResolver(store, applicationEndpoint), UnlockedContract: unlockedContractResolver(store, applicationEndpoint), ContractRevision: contractRevisionResolver(store), diff --git a/services/app-api/src/resolvers/questionResponse/index.ts b/services/app-api/src/resolvers/questionResponse/index.ts index 231774af57..2962b676f8 100644 --- a/services/app-api/src/resolvers/questionResponse/index.ts +++ b/services/app-api/src/resolvers/questionResponse/index.ts @@ -3,3 +3,4 @@ export { createContractQuestionResponseResolver } from './createContractQuestion export { questionResponseDocumentResolver } from './questionResponseDocumentResolver' export { createRateQuestionResolver } from './createRateQuestion' export { createRateQuestionResponseResolver } from './createRateQuestionResponse' +export { questionResolver } from './questionResolver' diff --git a/services/app-api/src/resolvers/questionResponse/questionResolver.ts b/services/app-api/src/resolvers/questionResponse/questionResolver.ts new file mode 100644 index 0000000000..e4666201c8 --- /dev/null +++ b/services/app-api/src/resolvers/questionResponse/questionResolver.ts @@ -0,0 +1,76 @@ +import type { Resolvers } from '../../gen/gqlServer' + +import type { ContractQuestionType } from '../../domain-models' +import type { Store } from '../../postgres' + +import { GraphQLError } from 'graphql' +export function questionResolver(store: Store): Resolvers['QuestionResolver'] { + return { + round: async (parent: ContractQuestionType) => { + const questions = await store.findAllQuestionsByContract( + parent.contractID + ) + if (questions instanceof Error) { + const errMessage = `Issue finding contract message: ${questions.message}` + + throw new GraphQLError(errMessage, { + extensions: { + code: 'INTERNAL_SERVER_ERROR', + cause: 'DB_ERROR', + }, + }) + } + + const divisionQuestions = questions + ?.filter((q) => q.division === parent.division) + .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) + + const matchingQuestion = divisionQuestions?.find( + (question) => question.id == parent.id + ) + + if (!matchingQuestion) { + return 0 + } else { + return divisionQuestions?.indexOf(matchingQuestion) !== + undefined + ? divisionQuestions?.indexOf(matchingQuestion) + 1 + : 0 + } + }, + // responses: async (parent: any) => { + // const questions = await store.findAllQuestionsByContract(parent.contractID) + // if (questions instanceof Error) { + // const errMessage = `Issue finding contract message: ${questions.message}` + + // throw new GraphQLError(errMessage, { + // extensions: { + // code: 'INTERNAL_SERVER_ERROR', + // cause: 'DB_ERROR', + // }, + // }) + // } + + // let round = 0 + // const divisionQuestions = questions?. + // filter((q) => q.division === parent.division). + // sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) + + // const matchingQuestion = divisionQuestions?.find( + // (question) => question.id == parent.id) + + // if (!matchingQuestion) { + // round = 0 + // } else { + // divisionQuestions?.indexOf(matchingQuestion) !== undefined + // ? round = divisionQuestions?.indexOf(matchingQuestion) + 1 + // : round = 0 + // } + // const responses = parent.responses.map((res: any) => { + // res.round = round + // return round + // }) + // return responses + // } + } +} From f2a7ad5322ca01a7525c8d10eda4bac82c488e28 Mon Sep 17 00:00:00 2001 From: pearl-truss Date: Tue, 21 Jan 2025 21:50:17 -0500 Subject: [PATCH 3/5] fix mocks --- packages/mocks/src/apollo/contractPackageDataMock.ts | 10 ++++++++++ packages/mocks/src/apollo/questionResponseDataMocks.ts | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/packages/mocks/src/apollo/contractPackageDataMock.ts b/packages/mocks/src/apollo/contractPackageDataMock.ts index 08da819027..0a0b9a7189 100644 --- a/packages/mocks/src/apollo/contractPackageDataMock.ts +++ b/packages/mocks/src/apollo/contractPackageDataMock.ts @@ -510,6 +510,7 @@ function mockContractPackageSubmittedWithQuestions( __typename: 'ContractQuestion' as const, id: 'dmco-question-1-id', contractID, + round: 1, createdAt: new Date('2022-12-16'), addedBy: mockValidCMSUser({ divisionAssignment: null, @@ -548,6 +549,7 @@ function mockContractPackageSubmittedWithQuestions( contractID, createdAt: new Date('2022-12-18'), addedBy: mockValidCMSUser() as CmsUser, + round: 1, documents: [ { s3URL: 's3://bucketname/key/dmco-question-2-document-1', @@ -576,6 +578,7 @@ function mockContractPackageSubmittedWithQuestions( id: 'dmcp-question-1-id', contractID, createdAt: new Date('2022-12-15'), + round: 1, addedBy: mockValidCMSUser({ divisionAssignment: 'DMCP', }) as CmsUser, @@ -617,6 +620,7 @@ function mockContractPackageSubmittedWithQuestions( id: 'oact-question-1-id', contractID, createdAt: new Date('2022-12-14'), + round: 1, addedBy: mockValidCMSUser({ divisionAssignment: 'OACT', }) as CmsUser, @@ -656,6 +660,7 @@ function mockContractPackageSubmittedWithQuestions( addedBy: mockValidCMSUser({ divisionAssignment: 'OACT', }) as CmsUser, + round: 2, documents: [ { s3URL: 's3://bucketname/key/oact-question-1-document-1', @@ -1508,6 +1513,7 @@ function mockContractPackageApprovedWithQuestions( addedBy: mockValidCMSUser({ divisionAssignment: null, }) as CmsUser, + round: 1, documents: [ { s3URL: 's3://bucketname/key/dmco-question-1-document-1', @@ -1542,6 +1548,7 @@ function mockContractPackageApprovedWithQuestions( contractID, createdAt: new Date('2022-12-18'), addedBy: mockValidCMSUser() as CmsUser, + round: 2, documents: [ { s3URL: 's3://bucketname/key/dmco-question-2-document-1', @@ -1570,6 +1577,7 @@ function mockContractPackageApprovedWithQuestions( id: 'dmcp-question-1-id', contractID, createdAt: new Date('2022-12-15'), + round: 1, addedBy: mockValidCMSUser({ divisionAssignment: 'DMCP', }) as CmsUser, @@ -1614,6 +1622,7 @@ function mockContractPackageApprovedWithQuestions( addedBy: mockValidCMSUser({ divisionAssignment: 'OACT', }) as CmsUser, + round: 1, documents: [ { s3URL: 's3://bucketname/key/oact-question-1-document-1', @@ -1647,6 +1656,7 @@ function mockContractPackageApprovedWithQuestions( id: 'oact-question-2-id', contractID: 'test-abc-123', createdAt: new Date('2022-12-17'), + round: 2, addedBy: mockValidCMSUser({ divisionAssignment: 'OACT', }) as CmsUser, diff --git a/packages/mocks/src/apollo/questionResponseDataMocks.ts b/packages/mocks/src/apollo/questionResponseDataMocks.ts index 0d4b057848..c55bba8a09 100644 --- a/packages/mocks/src/apollo/questionResponseDataMocks.ts +++ b/packages/mocks/src/apollo/questionResponseDataMocks.ts @@ -29,6 +29,7 @@ function mockQuestionsPayload( downloadURL: expect.any(String), }, ], + round: 1, division: 'DMCO', responses: [ { @@ -56,6 +57,7 @@ function mockQuestionsPayload( contractID, createdAt: new Date('2022-12-18'), addedBy: mockValidCMSUser() as CmsUser, + round: 1, documents: [ { s3URL: 's3://bucketname/key/dmco-question-2-document-1', @@ -110,6 +112,7 @@ function mockQuestionsPayload( }, ], division: 'DMCP', + round: 1, responses: [ { __typename: 'QuestionResponse' as const, @@ -143,6 +146,7 @@ function mockQuestionsPayload( addedBy: mockValidCMSUser({ divisionAssignment: 'OACT', }) as CmsUser, + round: 1, documents: [ { s3URL: 's3://bucketname/key/oact-question-1-document-1', From 689112c8d3a030b5e85080ce072697c8c750ade6 Mon Sep 17 00:00:00 2001 From: pearl-truss Date: Wed, 22 Jan 2025 11:19:03 -0500 Subject: [PATCH 4/5] Add resolver test, clean up resolver --- .../questionResponse/questionResolver.test.ts | 80 +++++++++++++++++++ .../questionResponse/questionResolver.ts | 52 +++--------- 2 files changed, 91 insertions(+), 41 deletions(-) create mode 100644 services/app-api/src/resolvers/questionResponse/questionResolver.test.ts diff --git a/services/app-api/src/resolvers/questionResponse/questionResolver.test.ts b/services/app-api/src/resolvers/questionResponse/questionResolver.test.ts new file mode 100644 index 0000000000..13d8b706d7 --- /dev/null +++ b/services/app-api/src/resolvers/questionResponse/questionResolver.test.ts @@ -0,0 +1,80 @@ +import { + constructTestPostgresServer, + createTestQuestion, +} from '../../testHelpers/gqlHelpers' +import { + testCMSUser, + createDBUsersWithFullData, +} from '../../testHelpers/userHelpers' +import { testS3Client } from '../../testHelpers/s3Helpers' +import { + createAndSubmitTestContract, + fetchTestContractWithQuestions, +} from '../../testHelpers' + +describe(`questionResolver`, () => { + const mockS3 = testS3Client() + const dmcpCMSUser = testCMSUser({ + divisionAssignment: 'DMCP', + }) + + beforeAll(async () => { + //Inserting a new CMS user, with division assigned, in postgres in order to create the question to user relationship. + await createDBUsersWithFullData([dmcpCMSUser]) + }) + + it('populates a round number on fetch', async () => { + const stateServer = await constructTestPostgresServer() + + const dmcpCMSServer = await constructTestPostgresServer({ + context: { + user: dmcpCMSUser, + }, + s3Client: mockS3, + }) + + const contract = await createAndSubmitTestContract(stateServer) + + const createdDMCPQuestion = await createTestQuestion( + dmcpCMSServer, + contract.id, + { + documents: [ + { + name: 'Test Question 2', + s3URL: 's3://bucketname/key/test12', + }, + ], + } + ) + + const createdDMCPQuestion2 = await createTestQuestion( + dmcpCMSServer, + contract.id, + { + documents: [ + { + name: 'Test Question 2', + s3URL: 's3://bucketname/key/test12', + }, + ], + } + ) + + const contractWithQuestions = await fetchTestContractWithQuestions( + stateServer, + contract.id + ) + const indexQuestionsResult = contractWithQuestions.questions + const firstDMCPQuestion = + indexQuestionsResult?.DMCPQuestions.edges.find( + (q) => q.node.id === createdDMCPQuestion.question.id + ) + const secondDMCPQuestion = + indexQuestionsResult?.DMCPQuestions.edges.find( + (q) => q.node.id === createdDMCPQuestion2.question.id + ) + expect(firstDMCPQuestion?.node.round).toBe(1) + expect(secondDMCPQuestion?.node.round).toBe(2) + }) +}) diff --git a/services/app-api/src/resolvers/questionResponse/questionResolver.ts b/services/app-api/src/resolvers/questionResponse/questionResolver.ts index e4666201c8..6fa77bf94a 100644 --- a/services/app-api/src/resolvers/questionResponse/questionResolver.ts +++ b/services/app-api/src/resolvers/questionResponse/questionResolver.ts @@ -2,16 +2,21 @@ import type { Resolvers } from '../../gen/gqlServer' import type { ContractQuestionType } from '../../domain-models' import type { Store } from '../../postgres' - import { GraphQLError } from 'graphql' + export function questionResolver(store: Store): Resolvers['QuestionResolver'] { return { round: async (parent: ContractQuestionType) => { const questions = await store.findAllQuestionsByContract( parent.contractID ) + if (!questions) { + throw new Error( + `Questions not found for contract: ${parent.contractID}` + ) + } if (questions instanceof Error) { - const errMessage = `Issue finding contract message: ${questions.message}` + const errMessage = `Issue return questions for contract message: ${questions.message}` throw new GraphQLError(errMessage, { extensions: { @@ -22,55 +27,20 @@ export function questionResolver(store: Store): Resolvers['QuestionResolver'] { } const divisionQuestions = questions - ?.filter((q) => q.division === parent.division) + .filter((q) => q.division === parent.division) .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) - const matchingQuestion = divisionQuestions?.find( + const matchingQuestion = divisionQuestions.find( (question) => question.id == parent.id ) if (!matchingQuestion) { return 0 } else { - return divisionQuestions?.indexOf(matchingQuestion) !== - undefined - ? divisionQuestions?.indexOf(matchingQuestion) + 1 + return divisionQuestions.indexOf(matchingQuestion) !== undefined + ? divisionQuestions.indexOf(matchingQuestion) + 1 : 0 } }, - // responses: async (parent: any) => { - // const questions = await store.findAllQuestionsByContract(parent.contractID) - // if (questions instanceof Error) { - // const errMessage = `Issue finding contract message: ${questions.message}` - - // throw new GraphQLError(errMessage, { - // extensions: { - // code: 'INTERNAL_SERVER_ERROR', - // cause: 'DB_ERROR', - // }, - // }) - // } - - // let round = 0 - // const divisionQuestions = questions?. - // filter((q) => q.division === parent.division). - // sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) - - // const matchingQuestion = divisionQuestions?.find( - // (question) => question.id == parent.id) - - // if (!matchingQuestion) { - // round = 0 - // } else { - // divisionQuestions?.indexOf(matchingQuestion) !== undefined - // ? round = divisionQuestions?.indexOf(matchingQuestion) + 1 - // : round = 0 - // } - // const responses = parent.responses.map((res: any) => { - // res.round = round - // return round - // }) - // return responses - // } } } From 0561e4ea2a8436bfc481e78eeb56f714c2a34d09 Mon Sep 17 00:00:00 2001 From: pearl-truss Date: Wed, 22 Jan 2025 11:21:17 -0500 Subject: [PATCH 5/5] remove change to domain model --- services/app-api/src/domain-models/QuestionsType.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/services/app-api/src/domain-models/QuestionsType.ts b/services/app-api/src/domain-models/QuestionsType.ts index 26c0df975d..be1466af72 100644 --- a/services/app-api/src/domain-models/QuestionsType.ts +++ b/services/app-api/src/domain-models/QuestionsType.ts @@ -16,7 +16,6 @@ const commonQuestionSchema = z.object({ division: divisionType, // DMCO, DMCP, OACT documents: z.array(document), responses: z.array(questionResponseType), - round: z.number(), }) const contractQuestion = commonQuestionSchema.extend({