Skip to content

Commit

Permalink
feat: add more myinfo sgid fields (#6766)
Browse files Browse the repository at this point in the history
* feat: use fe app URL for local dev redirect

* feat: add new myinfo sgid fields

new fields: sex, race, nationality, housingtype, hdbtype

* feat: add registered address

* feat: log if missing myinfo field is from sgid or singpass login

* feat: use isDataReadOnly property

* feat: add growthbook

* feat: add more sgid fields

* chore: promote -sgid config to use env site name

(cherry picked from commit e9d9288)

* fix: fix linting errors

* feat: change formatVehicles input type

* fix: set agency shortname as growthbook attribute

* fix: catch errors when parsing vehicles

* feat: account for blank fields

* feat: account for NA in missing field values check

* fix: clean up code

* fix: remove comment

* fix: include SGID_SUPPORTED_V1 in SGID_SUPPORTED_V1

* fix: include more details in comment

* feat: add logging to _isDataReadOnly for Singpass and SGID MyInfo

* fix: remove console.log statement

* feat: update mobile_number_with_country_code scope

* fix: return empty string when fieldValue is NA

---------

Co-authored-by: Ken <[email protected]>
  • Loading branch information
wanlingt and KenLSM authored Oct 17, 2023
1 parent 55ddc22 commit cfb4563
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .ebextensions/env-file-creation.config
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ files:
aws ssm get-parameter --name "${ENV_TYPE}-sentry" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_TYPE}-sms" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_TYPE}-ndi" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_TYPE}-sgid" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_TYPE}-verified-fields" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_TYPE}-webhook-verified-content" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_SITE_NAME}-sgid" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_SITE_NAME}-payment" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env
aws ssm get-parameter --name "${ENV_SITE_NAME}-cron-payment" --with-decryption --region $AWS_REGION | jq -r '.Parameter.Value' >> $TARGET_DIR/.env

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useCallback, useMemo } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import { Droppable } from 'react-beautiful-dnd'
import { Link as ReactLink } from 'react-router-dom'
import { Box, Text } from '@chakra-ui/react'
import { useFeatureIsOn, useGrowthBook } from '@growthbook/growthbook-react'

import { featureFlags } from '~shared/constants'
import {
AdminFormDto,
FormAuthType,
Expand Down Expand Up @@ -35,7 +37,7 @@ import { DraggableMyInfoFieldListOption } from '../FieldListOption'

import { FieldSection } from './FieldSection'

const SGID_SUPPORTED: Set<MyInfoAttribute> = new Set([
const SGID_SUPPORTED_V1 = [
MyInfoAttribute.Name,
MyInfoAttribute.DateOfBirth,
MyInfoAttribute.PassportNumber,
Expand All @@ -44,26 +46,64 @@ const SGID_SUPPORTED: Set<MyInfoAttribute> = new Set([
// phone number formats.
// MyInfo phone numbers support country code, while sgID-MyInfo does not.
// MyInfoAttribute.MobileNo,

// FRM-1189: This is disabled due to slight different formatting.
// We format the Myinfo response by separates lines in addresses with comma
// Whereas sgID separates each line with newline.
// This should be enabled in future work
// MyInfoAttribute.RegisteredAddress,
])

/**
* If sgID is used, checks if the corresponding
* MyInfo field is supported by sgID.
*/
const sgIDUnSupported = (
form: AdminFormDto | undefined,
fieldType: MyInfoAttribute,
): boolean =>
form?.authType === FormAuthType.SGID_MyInfo && !SGID_SUPPORTED.has(fieldType)
]
const SGID_SUPPORTED_V2 = [
...SGID_SUPPORTED_V1,
MyInfoAttribute.Sex,
MyInfoAttribute.Race,
MyInfoAttribute.Nationality,
MyInfoAttribute.HousingType,
MyInfoAttribute.HdbType,
MyInfoAttribute.RegisteredAddress,
MyInfoAttribute.BirthCountry,
MyInfoAttribute.VehicleNo,
MyInfoAttribute.Employment,
MyInfoAttribute.WorkpassStatus,
MyInfoAttribute.Marital,
MyInfoAttribute.MobileNo,
]

export const MyInfoFieldPanel = () => {
const { data: form, isLoading } = useCreateTabForm()

const { user } = useUser()

// FRM-1444: Remove once rollout is 100% and stable
const growthbook = useGrowthBook()

useEffect(() => {
if (growthbook) {
growthbook.setAttributes({
// Only update the `adminEmail` and `adminAgency` attributes, keep the rest the same
...growthbook.getAttributes(),
adminEmail: user?.email,
adminAgency: user?.agency.shortName,
})
}
}, [growthbook, user])

const showSgidMyInfoV2 = useFeatureIsOn(featureFlags.myinfoSgid)

const sgidSupportedFinal = useMemo(() => {
return showSgidMyInfoV2 ? SGID_SUPPORTED_V2 : SGID_SUPPORTED_V1
}, [showSgidMyInfoV2])

/**
* If sgID is used, checks if the corresponding
* MyInfo field is supported by sgID.
*/
const sgIDUnSupported = useCallback(
(form: AdminFormDto | undefined, fieldType: MyInfoAttribute): boolean => {
const sgidSupported: Set<MyInfoAttribute> = new Set(sgidSupportedFinal)

return (
form?.authType === FormAuthType.SGID_MyInfo &&
!sgidSupported.has(fieldType)
)
},
[sgidSupportedFinal],
)

// myInfo should be disabled if
// 1. form response mode is not email mode
// 2. form auth type is not myInfo
Expand All @@ -83,9 +123,8 @@ export const MyInfoFieldPanel = () => {
(fieldType: MyInfoAttribute): boolean => {
return isDisabled || sgIDUnSupported(form, fieldType)
},
[form, isDisabled],
[form, isDisabled, sgIDUnSupported],
)
const { user } = useUser()

return (
<>
Expand Down
1 change: 1 addition & 0 deletions shared/constants/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const featureFlags = {
'encryption-boundary-shift-hard-validation' as const,
encryptionBoundaryShiftVirusScanner:
'encryption-boundary-shift-virus-scanner' as const,
myinfoSgid: 'myinfo-sgid' as const,
}
11 changes: 11 additions & 0 deletions src/app/modules/myinfo/myinfo.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
MyInfoChildVaxxStatus,
MyInfoDataTransformer,
} from '../../../../shared/types'
import { createLoggerWithLabel } from '../../config/logger'

import {
formatAddress,
Expand All @@ -26,6 +27,8 @@ import {
} from './myinfo.format'
import { isMyInfoChildrenBirthRecords } from './myinfo.util'

const logger = createLoggerWithLabel(module)

/**
* Converts an internal MyInfo attribute used in FormSG to a scope
* which can be used to retrieve data from MyInfo.
Expand Down Expand Up @@ -422,6 +425,14 @@ export class MyInfoData
// Above cases should be exhaustive for all attributes supported by Form.
// Fall back to leaving field editable as data shape is unknown.
default:
logger.error({
message: 'Unknown attribute found in Singpass MyInfo field',
meta: {
action: '_isDataReadOnly',
myInfoValue,
attr,
},
})
return false
}
}
Expand Down
1 change: 1 addition & 0 deletions src/app/modules/myinfo/myinfo.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ export class MyInfoServiceClass {
fieldValue,
myInfoAttr,
myInfoConstantsList,
myInfoData,
)
}
}
Expand Down
13 changes: 11 additions & 2 deletions src/app/modules/myinfo/myinfo.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ import {
FormAuthNoEsrvcIdError,
FormNotFoundError,
} from '../form/form.errors'
import { SGIDMyInfoData } from '../sgid/sgid.adapter'
import { SGID_MYINFO_LOGIN_COOKIE_NAME } from '../sgid/sgid.constants'
import {
ProcessedChildrenResponse,
ProcessedFieldResponse,
} from '../submission/submission.types'

import { MyInfoData } from './myinfo.adapter'
import { MYINFO_LOGIN_COOKIE_NAME } from './myinfo.constants'
import {
MyInfoCookieStateError,
Expand Down Expand Up @@ -542,7 +544,7 @@ export const handleMyInfoChildHashResponse = (
*/
export const getMyInfoAttributeConstantsList = (
myInfoAttr: string | string[],
) => {
): string[] | undefined => {
switch (myInfoAttr) {
case MyInfoAttribute.Occupation:
return myInfoOccupations
Expand Down Expand Up @@ -576,15 +578,22 @@ export const logIfFieldValueNotInMyinfoList = (
fieldValue: string,
myInfoAttr: string | string[],
myInfoList: string[],
myInfoData: MyInfoData | SGIDMyInfoData,
) => {
const isFieldValueInMyinfoList = myInfoList.includes(fieldValue)
if (!isFieldValueInMyinfoList) {
const myInfoSource =
myInfoData instanceof MyInfoData ? 'Singpass MyInfo' : 'SGID MyInfo'
// SGID returns NA instead of empty field values, we don't need this to be logged
// as this is expected behaviour
const isNAFromSgid = myInfoAttr === 'SGID MyInfo' && fieldValue === 'NA'
if (!isNAFromSgid || !isFieldValueInMyinfoList) {
logger.error({
message: 'Myinfo field value not found in existing Myinfo constants list',
meta: {
action: 'prefillAndSaveMyInfoFields',
myInfoFieldValue: fieldValue,
myInfoAttr,
myInfoSource,
},
})
}
Expand Down
Loading

0 comments on commit cfb4563

Please sign in to comment.