Skip to content

Commit 90541fd

Browse files
authored
Release v1.33.0 (#862)
* FormSG local address field * Fix step position race condition * Show past execution steps even if step was deleted * Add execution back button * Update dependencies (`nanoid` and `express`) * Export ExecutionsForFlow page for admin dashboard
2 parents a8095f6 + 38511cd commit 90541fd

File tree

24 files changed

+866
-160
lines changed

24 files changed

+866
-160
lines changed

package-lock.json

Lines changed: 245 additions & 113 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"@bull-board/express": "5.17.0",
3535
"@graphql-tools/schema": "10.0.0",
3636
"@launchdarkly/node-server-sdk": "9.0.4",
37-
"@opengovsg/formsg-sdk": "0.11.0",
37+
"@opengovsg/formsg-sdk": "0.13.0",
3838
"@opengovsg/sgid-client": "2.1.0",
3939
"@plumber/types": "file:../types",
4040
"@taskforcesh/bullmq-pro": "7.7.1",
@@ -52,7 +52,7 @@
5252
"dotenv": "^10.0.0",
5353
"electrodb": "2.12.0",
5454
"email-validator": "2.0.4",
55-
"express": "4.19.2",
55+
"express": "4.21.2",
5656
"form-data": "4.0.0",
5757
"graphql": "16.8.1",
5858
"graphql-middleware": "6.1.35",
@@ -69,7 +69,7 @@
6969
"luxon": "2.5.2",
7070
"morgan": "^1.10.0",
7171
"multer": "1.4.5-lts.1",
72-
"nanoid": "3.3.6",
72+
"nanoid": "3.3.8",
7373
"objection": "^3.0.0",
7474
"p-limit": "3.1.0",
7575
"pg": "^8.7.1",
@@ -105,5 +105,5 @@
105105
"tsconfig-paths": "^4.2.0",
106106
"type-fest": "4.10.3"
107107
},
108-
"version": "1.32.3"
108+
"version": "1.33.0"
109109
}

packages/backend/src/apps/formsg/__tests__/auth/decrypt-form-response.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,4 +535,59 @@ describe('decrypt form response', () => {
535535
expect(mocks.getSdk).toBeCalledWith('staging')
536536
})
537537
})
538+
539+
describe('local address field', () => {
540+
it('should handle local address fields', async () => {
541+
$.flow.hasFileProcessingActions = false
542+
mocks.cryptoDecrypt.mockReturnValueOnce({
543+
responses: [
544+
{
545+
_id: 'addressFieldComplete',
546+
fieldType: 'address',
547+
question: 'What is your address?',
548+
answerArray: [
549+
'51',
550+
'BRAS BASAH ROAD',
551+
'Lazada One',
552+
'08',
553+
'888',
554+
'189554',
555+
],
556+
},
557+
{
558+
_id: 'addressFieldPartial',
559+
fieldType: 'address',
560+
question: 'What is your address?',
561+
answerArray: ['51', 'BRAS BASAH ROAD', '', '', '', '189554'],
562+
},
563+
],
564+
})
565+
566+
await expect(decryptFormResponse($)).resolves.toEqual(true)
567+
expect($.request.body).toEqual(
568+
expect.objectContaining({
569+
fields: {
570+
addressFieldComplete: {
571+
fieldType: 'address',
572+
question: 'What is your address?',
573+
answerArray: [
574+
'51',
575+
'BRAS BASAH ROAD',
576+
'Lazada One',
577+
'#08-888',
578+
'189554',
579+
],
580+
order: 1,
581+
},
582+
addressFieldPartial: {
583+
fieldType: 'address',
584+
question: 'What is your address?',
585+
answerArray: ['51', 'BRAS BASAH ROAD', '', '', '189554'],
586+
order: 2,
587+
},
588+
},
589+
}),
590+
)
591+
})
592+
})
538593
})

packages/backend/src/apps/formsg/__tests__/triggers/new-submission.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77

88
import { beforeEach, describe, expect, it } from 'vitest'
99

10+
import { ADDRESS_LABELS } from '../../common/constants'
1011
import trigger from '../../triggers/new-submission'
1112

1213
describe('new submission trigger', () => {
@@ -290,6 +291,24 @@ describe('new submission trigger for answer array fields', () => {
290291
order: 3,
291292
answerArray: ['weird', 'strange'],
292293
},
294+
addressFieldComplete: {
295+
question: 'What is your address?',
296+
fieldType: 'address',
297+
order: 4,
298+
answerArray: [
299+
'51',
300+
'BRAS BASAH ROAD',
301+
'Lazada One',
302+
'#08-888',
303+
'189554',
304+
],
305+
},
306+
addressFieldPartial: {
307+
question: 'What is your address?',
308+
fieldType: 'address',
309+
order: 5,
310+
answerArray: ['51', 'BRAS BASAH ROAD', '', '', '189554'], // Some empty fields
311+
},
293312
},
294313
},
295314
} as unknown as IExecutionStep
@@ -343,5 +362,25 @@ describe('new submission trigger for answer array fields', () => {
343362
const metadata = await trigger.getDataOutMetadata(executionStep)
344363
expect(metadata.fields.textFieldId3.answerArray).toBeUndefined()
345364
})
365+
366+
it('Address type: includes all address fields', async () => {
367+
const metadata = await trigger.getDataOutMetadata(executionStep)
368+
const addressMetadata = metadata.fields.addressFieldComplete
369+
.answerArray as IDataOutMetadatum[]
370+
371+
expect(addressMetadata).toHaveLength(5)
372+
ADDRESS_LABELS.forEach((label, index) => {
373+
expect(addressMetadata[index].label).toEqual(`Response 4, ${label}`)
374+
})
375+
})
376+
377+
it('Address type: includes all metadata for empty address fields', async () => {
378+
const metadata = await trigger.getDataOutMetadata(executionStep)
379+
const addressMetadata = metadata.fields.addressFieldPartial
380+
.answerArray as IDataOutMetadatum[]
381+
ADDRESS_LABELS.forEach((label, index) => {
382+
expect(addressMetadata[index].label).toEqual(`Response 5, ${label}`)
383+
})
384+
})
346385
})
347386
})

packages/backend/src/apps/formsg/auth/decrypt-form-response.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ import storeAttachmentInS3 from './helpers/store-attachment-in-s3'
1616

1717
const NRIC_VERIFIED_FIELDS = new Set(['sgidUinFin', 'uinFin'])
1818

19+
function processLocalAddress(answerArray: string[]): string[] {
20+
const answer = [...answerArray]
21+
22+
// Combine level number and unit number if both exist
23+
const levelNumber = answer[3]
24+
const unitNumber = answer[4]
25+
answer[3] = levelNumber && unitNumber ? `#${levelNumber}-${unitNumber}` : ''
26+
// Remove element at index 4 since we've combined it with element 3
27+
answer.splice(4, 1)
28+
29+
return answer
30+
}
31+
1932
/**
2033
* Filters NRIC if an NRIC filter was configured for the pipe.
2134
*
@@ -119,6 +132,10 @@ export async function decryptFormResponse(
119132
)
120133
}
121134

135+
if (rest.fieldType === 'address') {
136+
rest.answerArray = processLocalAddress(rest.answerArray as string[])
137+
}
138+
122139
// Note: the order may not be sequential; fields (e.g. NRIC) can be
123140
// omitted from the output.
124141
// Note: FormSG uses dot notation for field ids for MyInfo children
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,22 @@
11
export const FORM_ID_LENGTH = 24
2+
3+
/**
4+
* NOTE: this must be updated if there are any changes to the address fields
5+
* Currently following the same field names as FormSG
6+
*/
7+
export const ADDRESS_LABELS = [
8+
'Block number',
9+
'Street name',
10+
'Building name',
11+
// this combines level number and unit number
12+
'Unit number',
13+
'Postal code',
14+
]
15+
16+
// Reference form response for local address
17+
// ---
18+
// {
19+
// question: 'Local address',
20+
// fieldType: 'address',
21+
// answerArray: [ '51', 'BRAS BASAH ROAD', 'Lazada One', '8', '8888', '189554' ]
22+
// }

packages/backend/src/apps/formsg/triggers/new-submission/get-data-out-metadata.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
import logger from '@/helpers/logger'
1111
import { parseS3Id } from '@/helpers/s3'
1212

13+
import { ADDRESS_LABELS } from '../../common/constants'
14+
1315
function buildQuestionMetadatum(fieldData: IJSONObject): IDataOutMetadatum {
1416
const question: IDataOutMetadatum = {
1517
type: 'text',
@@ -57,8 +59,20 @@ function isAnswerArrayValid(fieldData: IJSONObject): boolean {
5759
if (!fieldData.answerArray) {
5860
return false
5961
}
60-
// strict check for only table and checkbox variables
61-
return fieldData.fieldType === 'table' || fieldData.fieldType === 'checkbox'
62+
// strict check for only table, checkbox and address variables
63+
const answerArrayFields = ['table', 'checkbox', 'address']
64+
return answerArrayFields.includes(fieldData.fieldType as string)
65+
}
66+
67+
function buildAnswerArrayForAddress(fieldData: IJSONObject): IDataOutMetadata {
68+
const { order } = fieldData
69+
70+
// NOTE: we return all labels as there are some optional fields in FormSG
71+
return ADDRESS_LABELS.map((label, index) => ({
72+
type: 'text',
73+
label: `Response ${order}, ${label}`,
74+
order: order ? `${order}.${index + 1}` : null,
75+
}))
6276
}
6377

6478
function buildAnswerArrayForCheckbox(
@@ -108,6 +122,8 @@ function buildAnswerArrayMetadatum(
108122
return buildAnswerArrayForCheckbox(fieldData)
109123
case 'table':
110124
return buildAnswerArrayForTable(fieldData)
125+
case 'address':
126+
return buildAnswerArrayForAddress(fieldData)
111127
default:
112128
logger.warn(`Answer array unknown fieldtype: ${fieldType}`, {
113129
fieldType,

packages/backend/src/apps/formsg/triggers/new-submission/get-mock-data.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ function generateVerifiedSubmitterInfoData(
6464
return {}
6565
}
6666

67+
function generateMockAddressData(): string[] {
68+
return ['51', 'BRAS BASAH ROAD', 'Lazada One', '#08-888', '189554']
69+
}
70+
6771
function generateMockPaymentData(products: Partial<PaymentProduct>[]) {
6872
// if there are no payment products, default to a mocked one
6973
const firstProduct: Partial<PaymentProduct> =
@@ -114,6 +118,11 @@ async function getMockData($: IGlobalVariable) {
114118
}
115119
}
116120

121+
if (data.responses[formFields[i]._id].fieldType === 'address') {
122+
data.responses[formFields[i]._id].answerArray =
123+
generateMockAddressData()
124+
}
125+
117126
if (data.responses[formFields[i]._id].fieldType === 'attachment') {
118127
data.responses[formFields[i]._id].answer = MOCK_ATTACHMENT_FILE_PATH
119128
}

0 commit comments

Comments
 (0)