Skip to content

Commit

Permalink
Merge pull request #7241 from opengovsg/release_v6.115.0
Browse files Browse the repository at this point in the history
* chore(deps-dev): bump @types/express from 4.17.17 to 4.17.21 (#7233)

Bumps [@types/express](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/express) from 4.17.17 to 4.17.21.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/express)

---
updated-dependencies:
- dependency-name: "@types/express"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(deps): bump nan from 2.17.0 to 2.19.0 (#7212)

Bumps [nan](https://github.com/nodejs/nan) from 2.17.0 to 2.19.0.
- [Changelog](https://github.com/nodejs/nan/blob/main/CHANGELOG.md)
- [Commits](nodejs/nan@v2.17.0...v2.19.0)

---
updated-dependencies:
- dependency-name: nan
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @types/json-stringify-safe from 5.0.0 to 5.0.3 (#7235)

Bumps [@types/json-stringify-safe](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/json-stringify-safe) from 5.0.0 to 5.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/json-stringify-safe)

---
updated-dependencies:
- dependency-name: "@types/json-stringify-safe"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(mrf): handling for incorrect submission secret key in url query params (#7219)

* fix: add secret key input mask (#7227)

* fix: add secret key input mask

* fix: add testid to allow storybook to capture inputfield with password type

* fix(mrf): workflow email UI cleanup (#7215)

* fix: mrf workflow email UI cleanup

* chore: update button formatting

* fix: more fixes

* chore: change port for react-email-preview to 4242

* chore: add comment on email defaults

* fix: potentially unsafe external url

* chore(mrf): remove flags (#7230)

chore: remove mrf flag checks

* chore(mrf): add announcement content (#7229)

* chore: add mrf announcement content

* chore: update mrf guide links

* chore: update date to 04-04

* chore: remove exclamation mark after url link

* fix: correct date validation for disabled fields (#7240)

* fix: correct date validation for disabled fields

* fix: removing console logs

* chore: bump version to v6.115.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justyn Oh <justynoh@gmail.com>
Co-authored-by: Kathleen Koh <89055608+kathleenkhy@users.noreply.github.com>
  • Loading branch information
4 people committed Apr 4, 2024
2 parents bcd965d + 8f02ef7 commit 16484da
Show file tree
Hide file tree
Showing 35 changed files with 8,439 additions and 548 deletions.
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,27 @@ All notable changes to this project will be documented in this file. Dates are d

Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

#### [v6.115.0](https://github.com/opengovsg/FormSG/compare/v6.114.1...v6.115.0)

- fix: correct date validation for disabled fields [`#7240`](https://github.com/opengovsg/FormSG/pull/7240)
- build: merge release v6.114.1 back to develop [`#7228`](https://github.com/opengovsg/FormSG/pull/7228)
- chore(mrf): add announcement content [`#7229`](https://github.com/opengovsg/FormSG/pull/7229)
- chore(mrf): remove flags [`#7230`](https://github.com/opengovsg/FormSG/pull/7230)
- fix(mrf): workflow email UI cleanup [`#7215`](https://github.com/opengovsg/FormSG/pull/7215)
- fix: add secret key input mask [`#7227`](https://github.com/opengovsg/FormSG/pull/7227)
- fix: correcting submission button bug for MRF [`#7232`](https://github.com/opengovsg/FormSG/pull/7232)
- fix(mrf): handling for incorrect submission secret key in url query params [`#7219`](https://github.com/opengovsg/FormSG/pull/7219)
- chore(deps-dev): bump @types/json-stringify-safe from 5.0.0 to 5.0.3 [`#7235`](https://github.com/opengovsg/FormSG/pull/7235)
- fix(deps): bump nan from 2.17.0 to 2.19.0 [`#7212`](https://github.com/opengovsg/FormSG/pull/7212)
- chore(deps-dev): bump @types/express from 4.17.17 to 4.17.21 [`#7233`](https://github.com/opengovsg/FormSG/pull/7233)

#### [v6.114.1](https://github.com/opengovsg/FormSG/compare/v6.114.0...v6.114.1)

> 3 April 2024

- build: release v6.114.0 [`#7226`](https://github.com/opengovsg/FormSG/pull/7226)
- fix: correcting submission button bug for MRF [`7342138`](https://github.com/opengovsg/FormSG/commit/73421387daf8f3a33c0030a43c81bb11190ee198)
- ref: genericize EndPageBlock [`017cfb3`](https://github.com/opengovsg/FormSG/commit/017cfb36457cbe293360c963f3d66257e5172907)
- chore: bump version to 6.114.1 [`c1c7ac2`](https://github.com/opengovsg/FormSG/commit/c1c7ac29e6bd8e2c3ab80b2fde22f84b778f1b4f)
- fix: correcting submission button bug for MRF [`96628aa`](https://github.com/opengovsg/FormSG/commit/96628aa3b4dd5abae00b429486139431a8b3bf05)

#### [v6.114.0](https://github.com/opengovsg/FormSG/compare/v6.113.0...v6.114.0)

Expand Down
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "form-frontend",
"version": "6.114.1",
"version": "6.115.0",
"homepage": ".",
"private": true,
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ export const SecretKeyVerificationInput = ({
<Stack direction="row" spacing="0.5rem">
<Skeleton isLoaded={!isLoading} w="100%">
<Input
data-testid="secretKey"
type="password"
isDisabled={isLoading}
{...register(SECRET_KEY_NAME, secretKeyValidationRules)}
/>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/constants/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const GUIDE_EMAIL_MODE = 'https://go.gov.sg/formsg-guide-email-mode'
export const GUIDE_STORAGE_MODE = 'https://go.gov.sg/formsg-guide-storage-mode'
export const GUIDE_FORM_LOGIC = 'https://go.gov.sg/formsg-guide-logic'
export const GUIDE_FORM_MRF = 'https://go.gov.sg/formsg-guide-mrf'
export const GUIDE_MRF_MODE = 'http://go.gov.sg/formsg-mrf'
export const GUIDE_SPCP_ESRVCID =
'https://go.gov.sg/formsg-guide-singpass-myinfo'
export const GUIDE_ENABLE_SPCP =
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/constants/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const LOCAL_STORAGE_EVENT = 'local-storage'
* Key to store whether a user has seen the rollout announcements before.
*/
export const ROLLOUT_ANNOUNCEMENT_KEY_PREFIX =
'has-seen-rollout-announcement-20231121-'
'has-seen-rollout-announcement-20240404-'

/**
* Key to store whether the admin has seen the feature tour in localStorage.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,25 +135,14 @@ export const StorageFormUnlocked = Template.bind({})
StorageFormUnlocked.parameters = StorageForm.parameters
StorageFormUnlocked.play = async ({ canvasElement }) => {
const canvas = within(canvasElement)
const inputName =
/enter or upload secret key your secret key was downloaded when you created your form/i

await waitFor(
async () => {
expect(
canvas.getByRole('textbox', {
name: inputName,
}),
).not.toBeDisabled()
expect(canvas.getByTestId('secretKey')).not.toBeDisabled()
},
{ timeout: 5000 },
)
await userEvent.type(
canvas.getByRole('textbox', {
name: inputName,
}),
MOCK_KEYPAIR.secretKey,
)
await userEvent.type(canvas.getByTestId('secretKey'), MOCK_KEYPAIR.secretKey)

await userEvent.click(
canvas.getByRole('button', { name: /unlock responses/i }),
Expand Down
94 changes: 79 additions & 15 deletions frontend/src/features/public-form/PublicFormProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from 'react'
import { Helmet } from 'react-helmet-async'
import { SubmitHandler } from 'react-hook-form'
import { useNavigate } from 'react-router-dom'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useDisclosure } from '@chakra-ui/react'
import { datadogLogs } from '@datadog/browser-logs'
import { useGrowthBook } from '@growthbook/growthbook-react'
Expand Down Expand Up @@ -36,6 +36,7 @@ import { MONGODB_ID_REGEX } from '~constants/routes'
import { useBrowserStm } from '~hooks/payments'
import { useTimeout } from '~hooks/useTimeout'
import { useToast } from '~hooks/useToast'
import { isKeypairValid } from '~utils/secretKeyValidation'
import { HttpError } from '~services/ApiService'
import { FormFieldValues } from '~templates/Field'

Expand Down Expand Up @@ -138,6 +139,7 @@ export const PublicFormProvider = ({
// Stop querying once submissionData is present.
/* enabled= */ !submissionData,
)

const {
data: encryptedPreviousSubmission,
isLoading: isSubmissionLoading,
Expand All @@ -149,8 +151,45 @@ export const PublicFormProvider = ({
/* enabled= */ !submissionData,
)

const isLoading = isFormLoading || isSubmissionLoading
const error = publicFormError || encryptedSubmissionError

const [previousSubmission, setPreviousSubmission] =
useState<ReturnType<typeof decryptSubmission>>()
const [isSubmissionSecretKeyInvalid, setIsSubmissionSecretKeyInvalid] =
useState(false)

const [searchParams] = useSearchParams()

if (
previousSubmissionId &&
encryptedPreviousSubmission &&
!previousSubmission &&
!isSubmissionSecretKeyInvalid
) {
let submissionSecretKey = ''
try {
submissionSecretKey = decodeURIComponent(searchParams.get('key') ?? '')
} catch (e) {
console.log(e)
}

const isValid = isKeypairValid(
encryptedPreviousSubmission.submissionPublicKey,
submissionSecretKey,
)

if (isValid) {
setPreviousSubmission(
decryptSubmission({
submission: encryptedPreviousSubmission,
secretKey: submissionSecretKey,
}),
)
} else {
setIsSubmissionSecretKeyInvalid(true)
}
}

// Replace form fields, logic, and workflow with the previous version for MRF consistency.
if (data && encryptedPreviousSubmission) {
Expand All @@ -161,9 +200,6 @@ export const PublicFormProvider = ({
}
}

const isLoading = isFormLoading || isSubmissionLoading
const error = publicFormError || encryptedSubmissionError

const growthbook = useGrowthBook()

useEffect(() => {
Expand Down Expand Up @@ -265,15 +301,35 @@ export const PublicFormProvider = ({
if (data) trackVisitPublicForm(data.form)
}, [data])

const isFormNotFound = useMemo(() => {
return (
(error instanceof HttpError &&
(error.code === 404 || error.code === 410)) ||
(!!previousSubmissionId &&
!!data &&
data.form.responseMode !== FormResponseMode.Multirespondent)
)
}, [data, error, previousSubmissionId])
const formNotFoundMessage = useMemo(() => {
// Server response 404 or 410
const isFormNotFound =
error instanceof HttpError && (error.code === 404 || error.code === 410)

// Non MRFs should not use the :formId/edit/:submissionId path
const isNonMultirespondentFormWithPreviousSubmissionId =
!!data &&
data.form.responseMode !== FormResponseMode.Multirespondent &&
!!previousSubmissionId

if (isFormNotFound || isNonMultirespondentFormWithPreviousSubmissionId) {
return {
title: 'Form not found',
header: 'This form is not available.',
message: error?.message || 'Form not found',
}
}

// Decryption failed for previous submission
if (isSubmissionSecretKeyInvalid) {
return {
title: 'Invalid form link',
header: 'This form link is no longer valid.',
message:
'A submission may have already been made using this link. If you think this is a mistake, please contact the agency that gave you the form link.',
}
}
}, [error, data, previousSubmissionId, isSubmissionSecretKeyInvalid])

const generateVfnExpiryToast = useCallback(() => {
if (vfnToastIdRef.current) {
Expand Down Expand Up @@ -713,8 +769,16 @@ export const PublicFormProvider = ({
...rest,
}}
>
<Helmet title={isFormNotFound ? 'Form not found' : data?.form.title} />
{isFormNotFound ? <FormNotFound message={error?.message} /> : children}
<Helmet
title={
formNotFoundMessage ? formNotFoundMessage.title : data?.form.title
}
/>
{formNotFoundMessage ? (
<FormNotFound {...formNotFoundMessage} />
) : (
children
)}
</PublicFormContext.Provider>
)
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
import { useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
import { Box } from '@chakra-ui/react'

import { FormAuthType, FormResponseMode } from '~shared/types'

import { isKeypairValid } from '~utils/secretKeyValidation'

import { usePublicFormContext } from '~features/public-form/PublicFormContext'
import { decryptSubmission } from '~features/public-form/utils/decryptSubmission'

import { FormAuth } from '../FormAuth'

import { FormFields } from './FormFields'
import { FormFieldsSkeleton } from './FormFieldsSkeleton'
import { SecretKeyVerification } from './SecretKeyVerification'

export const FormFieldsContainer = (): JSX.Element | null => {
const {
form,
previousSubmissionId,
isAuthRequired,
isLoading,
handleSubmitForm,
submissionData,
encryptedPreviousSubmission,
previousSubmission,
setPreviousSubmission,
} = usePublicFormContext()

const { submissionPublicKey = null, workflowStep } =
encryptedPreviousSubmission ?? {}
const [searchParams] = useSearchParams()
const queryParams = Object.fromEntries([...searchParams])
const { workflowStep } = encryptedPreviousSubmission ?? {}

const renderFields = useMemo(() => {
// Render skeleton when no data
Expand All @@ -49,48 +39,6 @@ export const FormFieldsContainer = (): JSX.Element | null => {
return <FormAuth authType={form.authType} />
}

// MRF
if (previousSubmissionId && !previousSubmission) {
let submissionSecretKey = ''
try {
submissionSecretKey = queryParams.key
? decodeURIComponent(queryParams.key || '')
: ''
} catch (e) {
console.log(e)
}

const isValid = isKeypairValid(
submissionPublicKey || '',
submissionSecretKey,
)

if (isValid) {
setPreviousSubmission(
decryptSubmission({
submission: encryptedPreviousSubmission,
secretKey: submissionSecretKey,
}),
)
}

return (
<SecretKeyVerification
publicKey={submissionPublicKey}
setSecretKey={(secretKey) =>
setPreviousSubmission(
decryptSubmission({
submission: encryptedPreviousSubmission,
secretKey,
}),
)
}
isLoading={isLoading}
prefillSecretKey={submissionSecretKey}
/>
)
}

return (
<FormFields
previousResponses={previousSubmission?.responses}
Expand All @@ -113,14 +61,9 @@ export const FormFieldsContainer = (): JSX.Element | null => {
isLoading,
form,
isAuthRequired,
previousSubmissionId,
previousSubmission,
workflowStep,
handleSubmitForm,
submissionPublicKey,
queryParams.key,
setPreviousSubmission,
encryptedPreviousSubmission,
])

if (submissionData) return null
Expand Down

This file was deleted.

0 comments on commit 16484da

Please sign in to comment.