diff --git a/.vscode/settings.json b/.vscode/settings.json index 9791ff2909..5ab2664b7d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "eslint.validate": ["javascript", "typescript"], "editor.formatOnSave": true, @@ -12,7 +12,7 @@ }, "[html]": { "editor.codeActionsOnSave": { - "source.fixAll.eslint": false + "source.fixAll.eslint": "never" }, "editor.defaultFormatter": "esbenp.prettier-vscode" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index bed7ebc951..5c7c0fc83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,22 @@ 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.111.0](https://github.com/opengovsg/FormSG/compare/v6.110.0...v6.111.0) + +- feat(fe): update copy, copy btn [`#7116`](https://github.com/opengovsg/FormSG/pull/7116) +- feat(virus-scanner): allow endpoint to be specified [`#7114`](https://github.com/opengovsg/FormSG/pull/7114) +- chore: remove Anguilla from country listing [`#7108`](https://github.com/opengovsg/FormSG/pull/7108) +- chore(vscode): update code actions value options [`#7113`](https://github.com/opengovsg/FormSG/pull/7113) +- feat(mrf): disable unsupported features [`#7106`](https://github.com/opengovsg/FormSG/pull/7106) +- fix: correct alignment issues in preview mode [`#7109`](https://github.com/opengovsg/FormSG/pull/7109) +- chore(OSS): add FerretDB migration instructions [`#7107`](https://github.com/opengovsg/FormSG/pull/7107) +- build: merge v6.110.0 back into develop [`#7105`](https://github.com/opengovsg/FormSG/pull/7105) +- build: release v6.110.0 [`#7104`](https://github.com/opengovsg/FormSG/pull/7104) + #### [v6.110.0](https://github.com/opengovsg/FormSG/compare/v6.109.0...v6.110.0) +> 21 February 2024 + - feat(mrf): retain workflow through a submission lifetime [`#7087`](https://github.com/opengovsg/FormSG/pull/7087) - feat(config): allow R2 bucket URLs [`#7097`](https://github.com/opengovsg/FormSG/pull/7097) - chore(deps): update ip package [`#7098`](https://github.com/opengovsg/FormSG/pull/7098) @@ -22,6 +36,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - build: merge release v6.109.0 into develop [`#7085`](https://github.com/opengovsg/FormSG/pull/7085) - chore(dev): update README with clearer virus-scanner install instructions [`#7083`](https://github.com/opengovsg/FormSG/pull/7083) - build: release v6.109.0 [`#7081`](https://github.com/opengovsg/FormSG/pull/7081) +- chore: bump version to v6.110.0 [`102cef3`](https://github.com/opengovsg/FormSG/commit/102cef33d7ae37e95c1b9f93f51712156cef1384) #### [v6.109.0](https://github.com/opengovsg/FormSG/compare/v6.108.0...v6.109.0) diff --git a/README.md b/README.md index e49b6778a8..d9f7165572 100755 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## Table of Contents - [Contributing](#contributing) - - [IMPORTANT NOTE TO ALL CONTRIBUTORS](#important-note-to-all-contributors) + - [IMPORTANT NOTE TO ALL CONTRIBUTORS](#important-note-to-all-contributors) - [Features](#features) - [Local Development (Docker)](#local-development-docker) - [Prerequisites](#prerequisites) @@ -31,6 +31,7 @@ - [MongoDB Scripts](#mongodb-scripts) - [Support](#support) - [Database Alternatives](#database-alternatives) + - [Migrating from MongoDB to FerretDB](#migrating-from-mongodb-to-ferretdb) - [Migrating from Mongoose ODM to Prisma ORM](#migrating-from-mongoose-odm-to-prisma-orm) - [Replacing MongoDB with CockroachDB](#replacing-mongodb-with-cockroachdb) - [Other Prisma supported DBs](#other-prisma-supported-dbs) @@ -225,6 +226,51 @@ Please contact FormSG (support@form.gov.sg) for any details. ## Database Alternatives +### Migrating from MongoDB to FerretDB +[FerretDB](https://ferretdb.io) is an open source MongoDB alternative built on PostgreSQL. MongoDB can be swapped out of FormSG for FerretDB. In order for this to be done, certain changes to the code should be made as described below: + +- Add postgres to the list of services in the `docker.compose` file e.g. + ``` pg: + image: postgres:15.3-alpine3.18 + environment: + - POSTGRES_USER= + - POSTGRES_PASSWORD= + - POSTGRES_DB= + volumes: + - pgdata:/var/lib/postgresql/data + ports: + - '5432:5432' +- In the same file, change the "database" image from MongoDB to FerretDB and update the database section to include the lines below: + ``` + image: ghcr.io/ferretdb/ferretdb:1.17.0 + environment: + - FERRETDB_TELEMETRY=disable + - FERRETDB_POSTGRESQL_URL=postgres://pg:5432/formsg?user=&password= + ports: + - '8080:8080' + depends_on: + - pg +- Lastly, add the *pgdata* volume + ``` + volumes: + mongodb_data: + driver: local + pgdata: +- FerretDB currently has some limitations and [certain database features are not supported](https://docs.ferretdb.io/reference/supported-commands/), these include TTL, database transactions and some aggregration pipelines which are all features used by FormSG. + + The following changes can be made to mitigate the limitations of FerretDB: + + - Add the *autoRemove: 'interval'* property to the initializing of the session object in the `session.ts` file. + - Remove the unsupported [aggregration pipeline stages](https://docs.ferretdb.io/reference/supported-commands/#aggregation-pipeline-stages) e.g. *lookup* and *project*, in the `submission.server.model.ts` file. + - Replace the *findOneAndUpdate* code block in the `user.server.model.ts` file with code similar to the one below: + ``` + const user = await this.exists({ email: upsertParams.email }) + if (!user) { + await this.create(upsertParams) + } + return this.findOne({ + email: upsertParams.email, + }).populate({... ### Migrating from Mongoose ODM to Prisma ORM FormSG uses Mongoose as the Object-Document Mapping (ODM) to MongoDB. This means that our code is strongly coupled with MongoDB as Mongoose solely supports it. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 68219c6d35..fbb2e7dc88 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "form-frontend", - "version": "6.110.0", + "version": "6.111.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "form-frontend", - "version": "6.110.0", + "version": "6.111.0", "hasInstallScript": true, "dependencies": { "@chakra-ui/react": "^1.8.6", diff --git a/frontend/package.json b/frontend/package.json index 45501cd674..b7ba9b626d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "form-frontend", - "version": "6.110.0", + "version": "6.111.0", "homepage": ".", "private": true, "dependencies": { diff --git a/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx b/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx index 53991ee8fe..966aac2551 100644 --- a/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx +++ b/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx @@ -47,7 +47,7 @@ interface PreviewFormBannerProps { const textProps: TextProps = { textStyle: 'body-2', color: 'white', - ml: '2rem', + mx: '2rem', mt: '0.5rem', mb: '0.5rem', } diff --git a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditEmail/EditEmail.tsx b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditEmail/EditEmail.tsx index dd456dfb0a..dfa29143a8 100644 --- a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditEmail/EditEmail.tsx +++ b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditEmail/EditEmail.tsx @@ -139,6 +139,18 @@ export const EditEmail = ({ field }: EditEmailProps): JSX.Element => { } }, [isPdfResponseEnabled]) + // vfn is not supported on MRF + const isToggleVfnDisabled = useMemo( + () => form?.responseMode === FormResponseMode.Multirespondent, + [form], + ) + + // email confirmation is not supported on MRF + const isToggleEmailConfirmationDisabled = useMemo( + () => form?.responseMode === FormResponseMode.Multirespondent, + [form], + ) + return ( @@ -158,7 +170,7 @@ export const EditEmail = ({ field }: EditEmailProps): JSX.Element => { - + { )} - + { const { data: freeSmsCount } = useFreeSmsQuota() const isToggleVfnDisabled = useMemo(() => { + // vfn is not supported on MRF + if (form?.responseMode === FormResponseMode.Multirespondent) return true if (!freeSmsCount) return true return ( !field.isVerifiable && !hasTwilioCredentials && freeSmsCount.freeSmsCounts >= freeSmsCount.quota ) - }, [field.isVerifiable, freeSmsCount, hasTwilioCredentials]) + }, [ + field.isVerifiable, + freeSmsCount, + hasTwilioCredentials, + form?.responseMode, + ]) const smsCountsDisclosure = useDisclosure() diff --git a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/BasicFieldPanel.tsx b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/BasicFieldPanel.tsx index 0e9667fc1b..b3c75d58e0 100644 --- a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/BasicFieldPanel.tsx +++ b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer/field-panels/BasicFieldPanel.tsx @@ -1,6 +1,9 @@ import { Droppable } from 'react-beautiful-dnd' import { Box } from '@chakra-ui/react' +import { BasicField, FormResponseMode } from '~shared/types' + +import { useAdminForm } from '~features/admin-form/common/queries' import { BASIC_FIELDS_ORDERED, CREATE_FIELD_DROP_ID, @@ -13,20 +16,33 @@ import { FieldSection } from './FieldSection' export const BasicFieldPanel = () => { const { isLoading } = useCreateTabForm() + const { data: form } = useAdminForm() return ( {(provided) => ( - {BASIC_FIELDS_ORDERED.map((fieldType, index) => ( - - ))} + {BASIC_FIELDS_ORDERED.map((fieldType, index) => { + let shouldDisableField = isLoading + + // Attachment is not supported on MRF + if ( + fieldType === BasicField.Attachment && + form?.responseMode === FormResponseMode.Multirespondent + ) { + shouldDisableField = true + } + + return ( + + ) + })} {provided.placeholder} diff --git a/frontend/src/features/public-form/components/FormEndPage/FormEndPage.stories.tsx b/frontend/src/features/public-form/components/FormEndPage/FormEndPage.stories.tsx index e33db29525..c135214869 100644 --- a/frontend/src/features/public-form/components/FormEndPage/FormEndPage.stories.tsx +++ b/frontend/src/features/public-form/components/FormEndPage/FormEndPage.stories.tsx @@ -43,6 +43,8 @@ Default.args = { title: 'Thank you for your submission with some super long backstory about how important the submission is to them', paragraph: 'We will get back to you shortly.\n\nOnce again,\r\nthank you.', + paymentTitle: '', + paymentParagraph: '', }, submissionData: { id: 'mockSubmissionId', @@ -84,7 +86,7 @@ ColorThemeOrange.args = { export const FeedbackSubmitted = Template.bind({}) FeedbackSubmitted.args = { ...Default.args, - isFeedbackSubmitted: true, + isFeedbackSectionHidden: true, } export const Mobile = Template.bind({}) diff --git a/frontend/src/features/public-form/components/FormEndPage/FormEndPage.tsx b/frontend/src/features/public-form/components/FormEndPage/FormEndPage.tsx index 798dea40e8..c3eb632e64 100644 --- a/frontend/src/features/public-form/components/FormEndPage/FormEndPage.tsx +++ b/frontend/src/features/public-form/components/FormEndPage/FormEndPage.tsx @@ -13,13 +13,13 @@ export interface FormEndPageProps { endPage: FormDto['endPage'] submissionData: SubmissionData handleSubmitFeedback: (inputs: FeedbackFormInput) => void - isFeedbackSubmitted: boolean + isFeedbackSectionHidden: boolean colorTheme: FormColorTheme } export const FormEndPage = ({ handleSubmitFeedback, - isFeedbackSubmitted, + isFeedbackSectionHidden, colorTheme, ...endPageProps }: FormEndPageProps): JSX.Element => { @@ -40,7 +40,7 @@ export const FormEndPage = ({ {...endPageProps} colorTheme={colorTheme} /> - {isFeedbackSubmitted ? null : ( + {isFeedbackSectionHidden ? null : ( { /> ) } + + const isFeedbackHidden = + // Feedback is not supported on MRF + form.responseMode === FormResponseMode.Multirespondent || + isFeedbackSubmitted + return ( { submissionData={submissionData} formTitle={form.title} endPage={form.endPage} - isFeedbackSubmitted={isFeedbackSubmitted} + isFeedbackSectionHidden={isFeedbackHidden} handleSubmitFeedback={handleSubmitFeedback} /> diff --git a/frontend/src/features/public-form/components/FormEndPage/components/PaymentEndPagePreview.tsx b/frontend/src/features/public-form/components/FormEndPage/components/PaymentEndPagePreview.tsx index 7daa3fb9d3..09a5dac02d 100644 --- a/frontend/src/features/public-form/components/FormEndPage/components/PaymentEndPagePreview.tsx +++ b/frontend/src/features/public-form/components/FormEndPage/components/PaymentEndPagePreview.tsx @@ -31,8 +31,9 @@ export const PaymentEndPagePreview = ({ {isFeedbackSubmitted ? null : ( diff --git a/frontend/src/features/public-form/components/FormPaymentPage/stripe/StripePaymentElement.tsx b/frontend/src/features/public-form/components/FormPaymentPage/stripe/StripePaymentElement.tsx index 8f5b491b8e..b90a493aa7 100644 --- a/frontend/src/features/public-form/components/FormPaymentPage/stripe/StripePaymentElement.tsx +++ b/frontend/src/features/public-form/components/FormPaymentPage/stripe/StripePaymentElement.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react' import { useParams } from 'react-router-dom' -import { Box, Center, Container } from '@chakra-ui/react' +import { Box, Center, Container, Flex, Stack, Text } from '@chakra-ui/react' import { Elements, useStripe } from '@stripe/react-stripe-js' import { loadStripe } from '@stripe/stripe-js' @@ -11,6 +11,7 @@ import { } from '~shared/types' import InlineMessage from '~components/InlineMessage' +import { CopyButton } from '~templates/CopyButton' import { useEnv } from '~features/env/queries' @@ -155,9 +156,22 @@ const StripePaymentContainer = ({ {secretEnv === 'production' ? null : ( - Use '4242 4242 4242 4242' as your card number to test payments - on this form. Payments made on this form will only show in - test mode in Stripe. + + + Make a test payment with the card number below! Payments + made on this form will only show in test mode in Stripe. + + + 4242 4242 4242 4242 + + + + + )} diff --git a/package-lock.json b/package-lock.json index 54fbe0b08e..1ddabfdde1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "FormSG", - "version": "6.110.0", + "version": "6.111.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "FormSG", - "version": "6.110.0", + "version": "6.111.0", "hasInstallScript": true, "dependencies": { "@aws-sdk/client-cloudwatch-logs": "^3.347.1", diff --git a/package.json b/package.json index 6845945fd2..cbb3f9108d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "FormSG", "description": "Form Manager for Government", - "version": "6.110.0", + "version": "6.111.0", "homepage": "https://form.gov.sg", "authors": [ "FormSG " diff --git a/shared/constants/countryRegion.ts b/shared/constants/countryRegion.ts index 393f342fc8..66da81c73e 100644 --- a/shared/constants/countryRegion.ts +++ b/shared/constants/countryRegion.ts @@ -5,7 +5,6 @@ export enum CountryRegion { American_Samoa = 'American Samoa', Andorra = 'Andorra', Angola = 'Angola', - Anguilla = 'Anguilla', Antigua = 'Antigua', Argentina = 'Argentina', Armenia = 'Armenia', diff --git a/shared/constants/field/myinfo/myinfo-countries.ts b/shared/constants/field/myinfo/myinfo-countries.ts index cc2e5efec6..d14bff865a 100644 --- a/shared/constants/field/myinfo/myinfo-countries.ts +++ b/shared/constants/field/myinfo/myinfo-countries.ts @@ -5,7 +5,6 @@ export const myInfoCountries = [ 'AMERICAN SAMOA', 'ANDORRA', 'ANGOLA', - 'ANGUILLA', 'ANTIGUA', 'ARGENTINA', 'ARMENIA', diff --git a/src/app/config/config.ts b/src/app/config/config.ts index 524f95ad72..f242b94ec4 100644 --- a/src/app/config/config.ts +++ b/src/app/config/config.ts @@ -97,10 +97,16 @@ const s3 = new aws.S3({ // using aws-sdk v3 (FRM-993) const virusScannerLambda = new Lambda({ region: basicVars.awsConfig.region, - // Endpoint is set for development mode to point to the separate docker container running the lambda function. + // For dev mode or where specified, endpoint is set to point to the separate docker container running the lambda function. // host.docker.internal is a special DNS name which resolves to the internal IP address used by the host. // Reference: https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host - ...(isDev ? { endpoint: 'http://host.docker.internal:9999' } : undefined), + ...(isDev || basicVars.awsConfig.virusScannerLambdaEndpoint + ? { + endpoint: + basicVars.awsConfig.virusScannerLambdaEndpoint || + 'http://host.docker.internal:9999', + } + : undefined), }) const awsConfig: AwsConfig = { diff --git a/src/app/config/schema.ts b/src/app/config/schema.ts index e17f25d980..ee6361f134 100644 --- a/src/app/config/schema.ts +++ b/src/app/config/schema.ts @@ -297,6 +297,12 @@ export const optionalVarsSchema: Schema = { default: '', env: 'VIRUS_SCANNER_LAMBDA_FUNCTION_NAME', }, + virusScannerLambdaEndpoint: { + doc: 'Endpoint address for virus scanner lambda function. Specify this if the lambda is hosted neither on AWS nor your local dev environment.', + format: String, + default: '', + env: 'VIRUS_SCANNER_LAMBDA_ENDPOINT', + }, }, core: { port: { diff --git a/src/types/config.ts b/src/types/config.ts index 0c85b6d056..e76271230b 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -168,6 +168,7 @@ export interface IOptionalVarsSchema { region: string customCloudWatchGroup: string virusScannerLambdaFunctionName: string + virusScannerLambdaEndpoint: string } mail: { from: string