Skip to content

Commit

Permalink
fixed webhooks to display separate inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
scottheng96 committed Jan 22, 2025
1 parent c0bea8b commit 7c33fb4
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const DecryptedAddressRow = ({ row }: DecryptedRowBaseProps): JSX.Element => {

export const DecryptedRow = memo(
({ row, attachmentDecryptionKey }: DecryptedRowProps): JSX.Element => {
console.log(row)
switch (row.fieldType) {
case BasicField.Section:
return <DecryptedHeaderRow row={row} />
Expand All @@ -133,8 +134,9 @@ export const DecryptedRow = memo(
)
case BasicField.Table:
return <DecryptedTableRow row={row} />
case BasicField.Address:
return <DecryptedAddressRow row={row} />
// case BasicField.Address:
// if (row.answer? typeof )
// return <DecryptedAddressRow row={row} />
default:
return (
<Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ import { useStorageResponsesContext } from '../ResponsesPage/storage'

import { DecryptedRow } from './DecryptedRow'
import { IndividualResponseNavbar } from './IndividualResponseNavbar'
import { useMutateDownloadAttachments } from './mutations'
import {
manageAddressResponseDisplay,
useMutateDownloadAttachments,
} from './mutations'
import { PaymentSection } from './PaymentSection'
import { useIndividualSubmission } from './queries'

Expand Down Expand Up @@ -141,6 +144,10 @@ export const IndividualResponsePage = (): JSX.Element => {
key: data?.submissionSecretKey || '',
})}`

// combine address fields into 1 display response

const responses = manageAddressResponseDisplay(data?.responses)

return (
<Flex flexDir="column" marginTop={{ base: '-1.5rem', md: '-3rem' }}>
<IndividualResponseNavbar />
Expand Down Expand Up @@ -209,7 +216,7 @@ export const IndividualResponsePage = (): JSX.Element => {
) : (
<>
<Stack spacing="1.5rem" divider={<StackDivider />}>
{data?.responses.map((r, idx) => (
{responses?.map((r, idx) => (
<DecryptedRow
row={r}
attachmentDecryptionKey={attachmentDecryptionKey}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { useCallback } from 'react'
import { useMutation } from 'react-query'
import FileSaver from 'file-saver'

import { BasicField } from '~shared/types'

import { useToast } from '~hooks/useToast'

import { AttachmentsDownloadMap } from '../ResponsesPage/storage/types'
import { AugmentedDecryptedResponse } from '../ResponsesPage/storage/utils/augmentDecryptedResponses'
import {
downloadAndDecryptAttachment,
downloadAndDecryptAttachmentsAsZip,
Expand Down Expand Up @@ -88,7 +91,7 @@ export const useMutateDownloadAttachments = () => {
* responses format:
* ["blockNumber_161","streetName_BUKIT BATOK STREET 11","buildingName_","levelNumber_","unitNumber_","postalCode_650161"]
*/
export const handleAddressResponseDisplay = (responses: string[]) => {
export const handleAddressResponseDisplay = (responses: string[]): string[] => {
const ans = responses
if (Array.isArray(ans)) {
let arr: string[] = []
Expand All @@ -104,14 +107,85 @@ export const handleAddressResponseDisplay = (responses: string[]) => {

// handle leve;/unit number additions
if (arr && arr[arr.length - 2] && arr[arr.length - 3]) {
const combinedUnit = '#' + arr[arr.length - 2] + '-' + arr[arr.length - 3]
const combinedUnit = '#' + arr[arr.length - 3] + '-' + arr[arr.length - 2]
arr.splice(arr.length - 3, 2, combinedUnit)
}

// remove empty inputs from array
const cleanedArr = arr.filter((item) => item !== '')
const cleanedArr = arr.filter((item) => item !== '').join(', ')

return cleanedArr
return [cleanedArr]
}
return responses
}

/**
* Finds address response group(s) of single responses (6) and combines them to a single
* response for display output. Manages multiple addresses if present, other fields are
* unchanged.
*/
export const manageAddressResponseDisplay = (
responses: AugmentedDecryptedResponse[] | undefined,
): AugmentedDecryptedResponse[] | undefined => {
if (!responses) return undefined
const result: AugmentedDecryptedResponse[] = []
const addressSubFields: AugmentedDecryptedResponse[] = []
for (const i in responses) {
if (responses[i].fieldType === BasicField.Address) {
if (!responses[i].answerArray) {
addressSubFields.push(responses[i])
if (addressSubFields.length === 6) {
// one full address field
const oneAddressField = combineOneAddressDisplay(addressSubFields)
result.push(oneAddressField)
addressSubFields.length = 0 //empty array for multiple addresses
}
} else {
const values = handleAddressResponseDisplay(
responses[i].answerArray as string[],
)
responses[i].answerArray = values
result.push(responses[i])
}
} else {
result.push(responses[i])
}
}
return result
}

/**
* Combines multiple address fields (SingleResponses) into 1 field for display
*/
const combineOneAddressDisplay = (
formFields: AugmentedDecryptedResponse[],
): AugmentedDecryptedResponse => {
if (formFields.length !== 6) {
throw Error('malformed address response field')
}
const id = formFields[0]._id
const fieldType = formFields[0].fieldType
const question = formFields[0].question.split('-')[0]
const questionNumber = formFields[0].questionNumber

const arr = formFields.map((ff) => {
return ff.answer
})
// update postal code; assumption that postalCode is already at the end
arr[arr.length - 1] = 'SINGAPORE ' + arr[arr.length - 1]
// update level/unit number
if (arr && arr[arr.length - 2] && arr[arr.length - 3]) {
const combinedUnit = '#' + arr[arr.length - 2] + '-' + arr[arr.length - 3]
arr.splice(arr.length - 3, 2, combinedUnit)
}
const answer = arr.filter((item) => item !== '').join(', ')

const r = {
_id: id,
fieldType: fieldType,
question: question,
answer: answer,
questionNumber: questionNumber,
}
return r
}
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,15 @@ describe('EncryptedResponseCsvGenerator', () => {
expectedSubmissionRow2,
])
})

// if('should handle submissions with address answerArray', () => {
// const mockDecryptedRecord = [
// generateRecord(
// 1,
// []
// )
// ]
// })
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,46 @@ export class EncryptedResponseCsvGenerator extends CsvGenerator {
* @throws Error when trying to convert record into a response instance. Should be caught in submissions client factory.
*/
addRecord({ record, created, submissionId }: DecryptedSubmissionData): void {
// const managedRecords = combineAddressSingleAnswers(record)
// const fieldRecords = record.map((content) => {
// const fieldRecord = getDecryptedResponseInstance(content)
// if (!fieldRecord.isHeader) {
// const currentMapping = this.fieldIdToQuestion.get(fieldRecord.id)
// // Only set new mapping if it does not exist or this record is a later
// // submission.
// // Might need to differentiate the question headers if we allow
// // signed-but-failed-verification rows to proceed.
// if (!currentMapping || created > currentMapping.created) {
// this.fieldIdToQuestion.set(fieldRecord.id, {
// created,
// question: fieldRecord.question,
// })
// }
// // Number of columns needed by this answer in the CSV
// const contentNumCols = fieldRecord.numCols
// // Number of columns currently allocated to the field
// const currentNumCols = this.fieldIdToNumCols[fieldRecord.id]
// // Update the number of columns allocated
// this.fieldIdToNumCols[fieldRecord.id] = currentNumCols
// ? Math.max(currentNumCols, contentNumCols)
// : contentNumCols
// }
// return fieldRecord
// })

// // Rearrange record to be an object identified by field ID.
// this.unprocessed.push({
// created,
// submissionId,
// record: keyBy(fieldRecords, (fieldRecord) => fieldRecord.id),
// })
// }
const fieldRecords: Response[] = []
// First pass, create object with { [fieldId]: question } from
// decryptedContent to get all the questions.
record.forEach((content) => {
//split address record (answerArray) into individual columns
if (content.fieldType.toString() === 'address') {
//split address record (answerArray, stored in MRF) into individual columns
if (content.fieldType.toString() === 'address' && content.answerArray) {
const addressFieldRecords: Response[] =
getAddressDecryptedResponseInstances(content) // returns a list of Responses
addressFieldRecords.forEach((fieldRecord) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SingleResponse,
TableResponse,
} from './csv-response-classes'

/**
* Converts a field record into a custom response instance
* @param fieldRecordData Field record
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/features/public-form/utils/createSubmission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ export const createClearSubmissionWithVirusScanningFormData = (
...formDataArgsRest,
}),
)
console.log(responses)
console.log(JSON.stringify(responses))

if (!isEmpty(attachments)) {
forOwn(attachments, (attachment, fieldId) => {
if (attachment) {
Expand Down
3 changes: 2 additions & 1 deletion shared/types/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export const AddressResponse = ResponseBase.extend({
export type AddressResponse = z.infer<typeof AddressResponse>

export const AddressSubFieldResponse = SingleAnswerResponse.extend({
fieldType: z.literal(BasicField.ShortText),
fieldType: z.literal(BasicField.Address),
})
export type AddressSubFieldResponse = z.infer<typeof AddressSubFieldResponse>

Expand Down Expand Up @@ -194,3 +194,4 @@ export type FieldResponse =
| ChildBirthRecordsResponse
| SingleChildSubRecordResponse
| AddressResponse
| AddressSubFieldResponse
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ export const submitEmailModeForm: ControllerHandler<
mode: 'email',
})
}

// Send response to admin
// NOTE: This should short circuit in the event of an error.
// This is why sendSubmissionToAdmin is separated from sendEmailConfirmations in 2 blocks
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StatusCodes } from 'http-status-codes'
import { compact } from 'lodash'

import { BasicField, FormAuthType } from '../../../../../shared/types'
import { BasicField, FormAuthType, ProductItemDto } from '../../../../../shared/types'

Check failure on line 4 in src/app/modules/submission/email-submission/email-submission.util.ts

View workflow job for this annotation

GitHub Actions / backend_lint

Replace `·BasicField,·FormAuthType,·ProductItemDto·` with `⏎··BasicField,⏎··FormAuthType,⏎··ProductItemDto,⏎`

Check failure on line 4 in src/app/modules/submission/email-submission/email-submission.util.ts

View workflow job for this annotation

GitHub Actions / backend_lint

'ProductItemDto' is defined but never used

Check failure on line 4 in src/app/modules/submission/email-submission/email-submission.util.ts

View workflow job for this annotation

GitHub Actions / backend_lint

Replace `·BasicField,·FormAuthType,·ProductItemDto·` with `⏎··BasicField,⏎··FormAuthType,⏎··ProductItemDto,⏎`

Check failure on line 4 in src/app/modules/submission/email-submission/email-submission.util.ts

View workflow job for this annotation

GitHub Actions / backend_lint

'ProductItemDto' is defined but never used
import {
EmailAdminDataField,
EmailDataCollationToolField,
Expand Down Expand Up @@ -79,7 +79,11 @@ import {
ProcessedFieldResponse,
ProcessedTableResponse,
} from '../submission.types'
import { getAnswersForChild, getMyInfoPrefix } from '../submission.utils'
import {
getAnswersForAddress,
getAnswersForChild,
getMyInfoPrefix,
} from '../submission.utils'

import {
ATTACHMENT_PREFIX,
Expand Down Expand Up @@ -462,8 +466,10 @@ const createFormattedDataForOneField = <T extends EmailDataFields | undefined>(
getFormattedFunction(childField, hashedFields),
)
} else if (isProcessedAddressResponse(response)) {
const address = getAnswerForAddress(response)
return [getFormattedFunction(address, hashedFields)]
return getAnswersForAddress(response).map((subField) =>
// splitting into single responses for JSON output (similar to webhook)
getFormattedFunction(subField, hashedFields),
)
} else {
return [getFormattedFunction(response, hashedFields)]
}
Expand Down Expand Up @@ -631,6 +637,18 @@ export class SubmissionEmailObj {
* Getter function to return formData which is used to send responses to admin
*/
get formData(): EmailAdminDataField[] {
// const managedResponse = this.parsedResponses.map((response) => {
// if (
// response.fieldType === BasicField.Address &&
// 'answerArray' in response
// ) {
// response.answerArray = handleAddressResponseDisplayEmail(
// response.answerArray,
// )
// }
// return response
// })

return this.parsedResponses.flatMap((response) =>
createFormattedDataForOneField(
response,
Expand All @@ -641,23 +659,32 @@ export class SubmissionEmailObj {
}
}

const handleAddressResponseDisplayForEmail = (responses: string[]) => {
export const handleAddressResponseDisplayEmail = (
responses: string[],
): string[] => {
const ans = responses
if (Array.isArray(ans)) {
let arr: string[] = []

// remove all address prefixes
if (ans.every((item) => typeof item === 'string')) {
arr = ans.map((item) => (item as string).split('_')[1])
}

// handle postal code additions
if (arr && arr[arr.length - 1])
arr[arr.length - 1] = 'SINGAPORE ' + arr[arr.length - 1]

// handle leve;/unit number additions
if (arr && arr[arr.length - 2] && arr[arr.length - 3]) {
const combinedUnit = '#' + arr[arr.length - 2] + '-' + arr[arr.length - 3]
const combinedUnit = '#' + arr[arr.length - 3] + '-' + arr[arr.length - 2]
arr.splice(arr.length - 3, 2, combinedUnit)
}

// remove empty inputs from array
const cleanedArr = arr.filter((item) => item !== '')
return cleanedArr.join(', ')
const cleanedArr = arr.filter((item) => item !== '').join(', ')

return [cleanedArr]
}
return responses.join(', ')
return responses
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ import {
StorageSubmissionMiddlewareHandlerType,
ValidateSubmissionMiddlewareHandlerRequest,
} from './encrypt-submission.types'
import { formatMyInfoStorageResponseData } from './encrypt-submission.utils'
import {
formatAddressResponseData,
formatMyInfoStorageResponseData,
} from './encrypt-submission.utils'
import IncomingEncryptSubmission from './IncomingEncryptSubmission.class'

const logger = createLoggerWithLabel(module)
Expand Down Expand Up @@ -436,6 +439,13 @@ export const validateStorageSubmission = async (
hashedFields,
)
req.body.responses = storageFormData
return { parsedResponses, hashedFields }
})
.map(({ parsedResponses }) => {
const storageFormData = formatAddressResponseData(
parsedResponses.getAllResponses(),
)
req.body.responses = storageFormData
return next()
})
.mapErr((error) => {
Expand Down
Loading

0 comments on commit 7c33fb4

Please sign in to comment.