Skip to content

Commit

Permalink
MCR-4449: Rate create rate question response api (#2824)
Browse files Browse the repository at this point in the history
* Add create rate response mutation and schema changes

* More renaming question to contractQuestion

* create rate question response postgres functions

* create rate question response resolver and tests

* A comment.
  • Loading branch information
JasonLin0991 authored Oct 10, 2024
1 parent 037d7ff commit ea1997e
Show file tree
Hide file tree
Showing 20 changed files with 398 additions and 32 deletions.
8 changes: 8 additions & 0 deletions services/app-api/src/postgres/postgresStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
insertContractQuestionResponse,
insertRateQuestion,
findAllQuestionsByRate,
insertRateQuestionResponse,
} from './questionResponse'
import { findAllSupportedStates } from './state'
import {
Expand Down Expand Up @@ -118,6 +119,11 @@ type Store = {
user: StateUserType
) => Promise<ContractQuestionType | Error>

insertRateQuestionResponse: (
questionInput: InsertQuestionResponseArgs,
user: StateUserType
) => Promise<RateQuestionType | Error>

insertRateQuestion: (
questionInput: CreateRateQuestionInputType,
user: CMSUsersUnionType
Expand Down Expand Up @@ -227,6 +233,8 @@ function NewPostgresStore(client: PrismaClient): Store {
insertContractQuestionResponse(client, questionInput, user),
insertRateQuestion: (questionInput, user) =>
insertRateQuestion(client, questionInput, user),
insertRateQuestionResponse: (questionInput, user) =>
insertRateQuestionResponse(client, questionInput, user),
findAllQuestionsByRate: (rateID) =>
findAllQuestionsByRate(client, rateID),

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { PrismaClient } from '@prisma/client'
import type { ContractQuestionType } from '../../domain-models'
import { questionPrismaToDomainType, questionInclude } from './questionHelpers'
import {
contractQuestionPrismaToDomainType,
questionInclude,
} from './questionHelpers'

export async function findAllQuestionsByContract(
client: PrismaClient,
Expand All @@ -18,7 +21,7 @@ export async function findAllQuestionsByContract(
})

return findResult.map((question) =>
questionPrismaToDomainType(question)
contractQuestionPrismaToDomainType(question)
)
} catch (e: unknown) {
if (e instanceof Error) {
Expand Down
3 changes: 2 additions & 1 deletion services/app-api/src/postgres/questionResponse/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ export { findAllQuestionsByContract } from './findAllQuestionsByContract'
export { insertContractQuestion } from './insertContractQuestion'
export {
convertToIndexQuestionsPayload,
questionPrismaToDomainType,
contractQuestionPrismaToDomainType,
rateQuestionPrismaToDomainType,
convertToIndexRateQuestionsPayload,
} from './questionHelpers'
export { insertContractQuestionResponse } from './insertContractQuestionResponse'
export { insertRateQuestion } from './insertRateQuestion'
export { findAllQuestionsByRate } from './findAllQuestionsByRate'
export { insertRateQuestionResponse } from './insertRateQuestionResponse'
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import type {
DivisionType,
CMSUsersUnionType,
} from '../../domain-models'
import { questionPrismaToDomainType, questionInclude } from './questionHelpers'
import {
contractQuestionPrismaToDomainType,
questionInclude,
} from './questionHelpers'

export async function insertContractQuestion(
client: PrismaClient,
Expand Down Expand Up @@ -38,7 +41,7 @@ export async function insertContractQuestion(
include: questionInclude,
})

return questionPrismaToDomainType(result)
return contractQuestionPrismaToDomainType(result)
} catch (e) {
return e
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import type {
StateUserType,
ContractQuestionType,
} from '../../domain-models'
import { questionInclude, questionPrismaToDomainType } from './questionHelpers'
import {
questionInclude,
contractQuestionPrismaToDomainType,
} from './questionHelpers'
import { NotFoundError } from '../postgresErrors'

export async function insertContractQuestionResponse(
Expand Down Expand Up @@ -39,15 +42,14 @@ export async function insertContractQuestionResponse(
include: questionInclude,
})

return questionPrismaToDomainType(result)
return contractQuestionPrismaToDomainType(result)
} catch (e) {
// Return a NotFoundError if prisma fails on the primary key constraint
// An operation failed because it depends on one or more records
// that were required but not found.
if (e.code === 'P2025') {
return new NotFoundError('Question was not found to respond to')
}

return e
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { PrismaClient } from '@prisma/client'
import type {
InsertQuestionResponseArgs,
RateQuestionType,
StateUserType,
} from '../../domain-models'
import {
questionInclude,
rateQuestionPrismaToDomainType,
} from './questionHelpers'
import { NotFoundError } from '../postgresErrors'

export async function insertRateQuestionResponse(
client: PrismaClient,
response: InsertQuestionResponseArgs,
user: StateUserType
): Promise<RateQuestionType | Error> {
const documents = response.documents.map((document) => ({
name: document.name,
s3URL: document.s3URL,
}))

try {
const result = await client.rateQuestion.update({
where: {
id: response.questionID,
},
data: {
responses: {
create: {
addedBy: {
connect: {
id: user.id,
},
},
documents: {
create: documents,
},
},
},
},
include: questionInclude,
})

return rateQuestionPrismaToDomainType(result)
} catch (e) {
// Return a NotFoundError if prisma fails on the primary key constraint
// An operation failed because it depends on one or more records
// that were required but not found.
if (e.code === 'P2025') {
return new NotFoundError('Question was not found to respond to')
}
return e
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const commonQuestionPrismaToDomainType = <
responses: prismaQuestion.responses as QuestionResponseType[],
}) as unknown as R

const questionPrismaToDomainType = (
const contractQuestionPrismaToDomainType = (
prismaQuestion: PrismaQuestionType
): ContractQuestionType => commonQuestionPrismaToDomainType(prismaQuestion)
const rateQuestionPrismaToDomainType = (
Expand Down Expand Up @@ -92,7 +92,7 @@ const convertToIndexRateQuestionsPayload = (

export {
questionInclude,
questionPrismaToDomainType,
contractQuestionPrismaToDomainType,
convertToIndexQuestionsPayload,
convertToIndexRateQuestionsPayload,
rateQuestionPrismaToDomainType,
Expand Down
3 changes: 3 additions & 0 deletions services/app-api/src/resolvers/configureResolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
createContractQuestionResponseResolver,
questionResponseDocumentResolver,
createRateQuestionResolver,
createRateQuestionResponseResolver,
} from './questionResponse'
import {
fetchCurrentUserResolver,
Expand Down Expand Up @@ -138,6 +139,8 @@ export function configureResolvers(
launchDarkly
),
createRateQuestion: createRateQuestionResolver(store),
createRateQuestionResponse:
createRateQuestionResponseResolver(store),
createAPIKey: createAPIKeyResolver(jwt),
unlockRate: unlockRate(store),
submitRate: submitRate(store, launchDarkly),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { getTestStateAnalystsEmails } from '../../testHelpers/parameterStoreHelp
import { testLDService } from '../../testHelpers/launchDarklyHelpers'
import { sharedTestPrismaClient } from '../../testHelpers/storeHelpers'

describe('createQuestionResponse', () => {
describe('createContractQuestionResponse', () => {
const cmsUser = testCMSUser()
beforeAll(async () => {
//Inserting a new CMS user, with division assigned, in postgres in order to create the question to user relationship.
Expand Down Expand Up @@ -88,7 +88,7 @@ describe('createQuestionResponse', () => {
expect(createResponseResult).toBeDefined()
expect(assertAnErrorCode(createResponseResult)).toBe('BAD_USER_INPUT')
expect(assertAnError(createResponseResult).message).toBe(
`Question with ID: ${fakeID} not found to attach response to`
`Contract question with ID: ${fakeID} not found to attach response to`
)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,25 @@ export function createContractQuestionResponseResolver(
): MutationResolvers['createContractQuestionResponse'] {
return async (_parent, { input }, context) => {
const { user, ctx, tracer } = context
const span = tracer?.startSpan('createQuestionResponse', {}, ctx)
const span = tracer?.startSpan(
'createContractQuestionResponse',
{},
ctx
)

const featureFlags = await launchDarkly.allFlags(context)
const readStateAnalystsFromDBFlag =
featureFlags?.['read-write-state-assignments']
if (!isStateUser(user)) {
const msg = 'user not authorized to create a question response'
logError('createQuestionResponse', msg)
logError('createContractQuestionResponse', msg)
setErrorAttributesOnActiveSpan(msg, span)
throw new ForbiddenError(msg)
}

if (input.documents.length === 0) {
const msg = 'question response documents are required'
logError('createQuestionResponse', msg)
logError('createContractQuestionResponse', msg)
setErrorAttributesOnActiveSpan(msg, span)
throw new UserInputError(msg)
}
Expand All @@ -58,14 +62,14 @@ export function createContractQuestionResponseResolver(

if (createResponseResult instanceof Error) {
if (createResponseResult instanceof NotFoundError) {
const errMessage = `Question with ID: ${input.questionID} not found to attach response to`
logError('createQuestionResponse', errMessage)
const errMessage = `Contract question with ID: ${input.questionID} not found to attach response to`
logError('createContractQuestionResponse', errMessage)
setErrorAttributesOnActiveSpan(errMessage, span)
throw new UserInputError(errMessage)
}

const errMessage = `Issue creating question response for question ${input.questionID}. Message: ${createResponseResult.message}`
logError('createQuestionResponse', errMessage)
const errMessage = `Issue creating question response for contract question ${input.questionID}. Message: ${createResponseResult.message}`
logError('createContractQuestionResponse', errMessage)
setErrorAttributesOnActiveSpan(errMessage, span)
throw new Error(errMessage)
}
Expand All @@ -75,7 +79,7 @@ export function createContractQuestionResponseResolver(
)
if (questions instanceof Error) {
const errMessage = `Issue finding all questions for contract with ID ${createResponseResult.contractID}. Message: ${questions.message}`
logError('createQuestionResponse', errMessage)
logError('createContractQuestionResponse', errMessage)
setErrorAttributesOnActiveSpan(errMessage, span)
throw new GraphQLError(errMessage, {
extensions: {
Expand All @@ -91,15 +95,15 @@ export function createContractQuestionResponseResolver(
if (contract instanceof Error) {
if (contract instanceof NotFoundError) {
const errMessage = `Package with id ${createResponseResult.contractID} does not exist`
logError('createQuestionResponse', errMessage)
logError('createContractQuestionResponse', errMessage)
setErrorAttributesOnActiveSpan(errMessage, span)
throw new GraphQLError(errMessage, {
extensions: { code: 'NOT_FOUND' },
})
}

const errMessage = `Issue finding a package. Message: ${contract.message}`
logError('createQuestionResponse', errMessage)
logError('createContractQuestionResponse', errMessage)
setErrorAttributesOnActiveSpan(errMessage, span)
throw new GraphQLError(errMessage, {
extensions: {
Expand All @@ -111,7 +115,7 @@ export function createContractQuestionResponseResolver(

const statePrograms = store.findStatePrograms(contract.stateCode)
if (statePrograms instanceof Error) {
logError('createQuestionResponse', statePrograms.message)
logError('createContractQuestionResponse', statePrograms.message)
setErrorAttributesOnActiveSpan(statePrograms.message, span)
throw new GraphQLError(statePrograms.message, {
extensions: {
Expand Down Expand Up @@ -217,7 +221,7 @@ export function createContractQuestionResponseResolver(
})
}

logSuccess('createQuestionResponse')
logSuccess('createContractQuestionResponse')
setSuccessAttributesOnActiveSpan(span)

return {
Expand Down
Loading

0 comments on commit ea1997e

Please sign in to comment.