From 651e0abec526d0e5165b17aa3974f61ac0362897 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 14 Jun 2021 12:16:51 +0800 Subject: [PATCH 01/23] feat(configure-mobile.client.directive): updated modal to use new messages with hard-coded data --- .../configure-mobile.client.directive.js | 22 ++++++++++++------- .../admin/views/pop-up.client.modal.html | 2 +- src/public/translations/en-SG/main.json | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js b/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js index ee0bcec6a0..ed57ac9640 100644 --- a/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js +++ b/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js @@ -20,8 +20,8 @@ function configureMobileDirective() { '$translate', function ($uibModal, $scope, $translate) { // Get support form link from translation json. - $translate('LINKS.SUPPORT_FORM_LINK').then((supportFormLink) => { - $scope.supportFormLink = supportFormLink + $translate('LINKS.TWILIO_SETUP_LINK').then((twilioSetupLink) => { + $scope.twilioSetupLink = twilioSetupLink }) $scope.openVerifiedSMSModal = function () { @@ -39,13 +39,19 @@ function configureMobileDirective() { resolve: { externalScope: function () { return { - title: 'Verified SMS charges', - confirmButtonText: 'OK, Noted', + title: + 'OTP verification will be disabled at 10,000 responses', + confirmButtonText: 'Accept', description: ` - Under 10,000 form responses: Free verified SMS -

- Above 10,000 form responses: ~US$0.0395 per SMS - contact us - for billing. Forms exceeding the free tier without billing will be deactivated. + We provide SMS OTP verification for free up to 10,000 responses. OTP verification will be automatically disabled when your account reaches 10,000 responses. +

+ If you require OTP verification for more than 10,000 responses, + please arrange advance billing with us. + +

+ Current response count: ${0}/${0} `, } }, diff --git a/src/public/modules/forms/admin/views/pop-up.client.modal.html b/src/public/modules/forms/admin/views/pop-up.client.modal.html index ced8b7367c..39718f2501 100644 --- a/src/public/modules/forms/admin/views/pop-up.client.modal.html +++ b/src/public/modules/forms/admin/views/pop-up.client.modal.html @@ -6,7 +6,7 @@ diff --git a/src/public/translations/en-SG/main.json b/src/public/translations/en-SG/main.json index 746d70d70f..b84869dd4a 100644 --- a/src/public/translations/en-SG/main.json +++ b/src/public/translations/en-SG/main.json @@ -18,6 +18,7 @@ "GO_GOV": "https://go.gov.sg", "POSTMAN_GOV": "https://postman.gov.sg", "SUPPORT_FORM_LINK": "https://go.gov.sg/formsg-support", + "TWILIO_SETUP_LINK": "https://go.gov.sg/form-twilio-setup", "WHITELISTED_ATTACHMENT_TYPES": "https://go.gov.sg/formsg-cwl", "SINGPASS_ELIGIBILITY_FAQ": "https://www.ifaq.gov.sg/SINGPASS/apps/Fcd_faqmain.aspx#FAQ_2101385", "ESERVICE_ID_FAQ": "https://go.gov.sg/formsg-spcp", From dbc01269ca0935093b4fe7886e93707b6866c10d Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 14 Jun 2021 12:52:49 +0800 Subject: [PATCH 02/23] refactor(verification.constants): shifted sms_verification_limit to shared/util/verification --- src/app/services/mail/mail.service.ts | 5 ++++- src/shared/util/verification.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/services/mail/mail.service.ts b/src/app/services/mail/mail.service.ts index 686dd9d915..babf4ab9c6 100644 --- a/src/app/services/mail/mail.service.ts +++ b/src/app/services/mail/mail.service.ts @@ -5,7 +5,10 @@ import Mail from 'nodemailer/lib/mailer' import promiseRetry from 'promise-retry' import validator from 'validator' -import { HASH_EXPIRE_AFTER_SECONDS } from '../../../shared/util/verification' +import { + HASH_EXPIRE_AFTER_SECONDS, + SMS_VERIFICATION_LIMIT, +} from '../../../shared/util/verification' import { BounceType, EmailAdminDataField, diff --git a/src/shared/util/verification.ts b/src/shared/util/verification.ts index 3f361a96a3..31dd1a5a83 100644 --- a/src/shared/util/verification.ts +++ b/src/shared/util/verification.ts @@ -21,3 +21,5 @@ export enum VfnErrors { TransactionNotFound = 'TRANSACTION_NOT_FOUND', InvalidMobileNumber = 'INVALID_MOBILE_NUMBER', } + +export const SMS_VERIFICATION_LIMIT = 10000 From 380fa4bfe7b2db5f7f4c72988716addb58997396 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 14 Jun 2021 17:23:23 +0800 Subject: [PATCH 03/23] feat(configure-mobile.client): added frontend ui for toggling on/off --- .../configure-mobile.client.view.html | 20 ++++++- .../configure-mobile.client.directive.js | 54 ++++++++++++++++--- .../base/componentViews/verifiable-field.html | 5 +- src/shared/util/verification.ts | 6 +++ 4 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html b/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html index 44b68cf7fc..01e7ffcffe 100644 --- a/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html +++ b/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html @@ -11,7 +11,7 @@

+
+ + + + You have exceeded the limit for sms verification! + + Please arrange advance billing with us. + + + +
diff --git a/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js b/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js index ed57ac9640..028cba891d 100644 --- a/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js +++ b/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js @@ -1,5 +1,18 @@ 'use strict' +const { + ADMIN_VERIFIED_SMS_STATES, + SMS_VERIFICATION_LIMIT, +} = require('../../../../../shared/util/verification') + +// TODO: Implement this +const getSmsCounts = () => { + return Promise.resolve({ + verifiedSmsCount: 1000000, + messageServiceId: 'asdf', + }) +} + angular .module('forms') .directive('configureMobileDirective', [configureMobileDirective]) @@ -10,24 +23,55 @@ function configureMobileDirective() { 'modules/forms/admin/directiveViews/configure-mobile.client.view.html', restrict: 'E', scope: { - field: '=', + field: '<', name: '=', characterLimit: '=', }, controller: [ + '$q', '$uibModal', '$scope', '$translate', - function ($uibModal, $scope, $translate) { + function ($q, $uibModal, $scope, $translate) { // Get support form link from translation json. $translate('LINKS.TWILIO_SETUP_LINK').then((twilioSetupLink) => { $scope.twilioSetupLink = twilioSetupLink }) + const getAdminVerifiedSmsState = (verifiedSmsCount, msgSrvcId) => { + if (msgSrvcId) { + return ADMIN_VERIFIED_SMS_STATES.hasMessageServiceId + } + if (verifiedSmsCount < SMS_VERIFICATION_LIMIT) { + return ADMIN_VERIFIED_SMS_STATES.belowLimit + } + return ADMIN_VERIFIED_SMS_STATES.limitExceeded + } + + $q.when(getSmsCounts()).then( + ({ verifiedSmsCount, messageServiceId }) => { + $scope.verifiedSmsCount = verifiedSmsCount + $scope.messageServiceId = messageServiceId + $scope.adminVerifiedSmsState = getAdminVerifiedSmsState( + verifiedSmsCount, + messageServiceId, + ) + // NOTE: This links into the verifiable field component and hence, is used by both email and mobile + $scope.field.hasAdminExceededSmsLimit = + $scope.adminVerifiedSmsState === + ADMIN_VERIFIED_SMS_STATES.limitExceeded + }, + ) + + // Only open if the admin has sms counts below the limit. + // If the admin has counts above limit without a message id, the toggle should be disabled anyway. + // Otherwise, if the admin has a message id, just enable it without the modal $scope.openVerifiedSMSModal = function () { const isTogglingOnVerifiedSms = !$scope.field.isVerifiable $scope.verifiedSMSModal = isTogglingOnVerifiedSms && + $scope.adminVerifiedSmsState === + ADMIN_VERIFIED_SMS_STATES.belowLimit && $uibModal.open({ animation: true, backdrop: 'static', @@ -46,12 +90,10 @@ function configureMobileDirective() { We provide SMS OTP verification for free up to 10,000 responses. OTP verification will be automatically disabled when your account reaches 10,000 responses.

If you require OTP verification for more than 10,000 responses, - please arrange advance billing with us. + please arrange advance billing with us.

- Current response count: ${0}/${0} + Current response count: ${$scope.verifiedSmsCount}/${SMS_VERIFICATION_LIMIT} `, } }, diff --git a/src/public/modules/forms/base/componentViews/verifiable-field.html b/src/public/modules/forms/base/componentViews/verifiable-field.html index 17e191a573..592dd13951 100644 --- a/src/public/modules/forms/base/componentViews/verifiable-field.html +++ b/src/public/modules/forms/base/componentViews/verifiable-field.html @@ -1,7 +1,10 @@ -
+

-
+
You have exceeded the limit for sms verification! From 809a8915b6f8252172599b1617550345081e0383 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Wed, 23 Jun 2021 01:01:33 +0800 Subject: [PATCH 07/23] fix(configure-mobile.client.view): updated form twilio account link --- .../admin/directiveViews/configure-mobile.client.view.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html b/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html index 7a8487b5d2..84fe5b100f 100644 --- a/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html +++ b/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html @@ -37,7 +37,7 @@ You have exceeded the limit for sms verification! - + Please arrange advance billing with us. From 92f8a3a8a06a00abff4cfe0230cfbb3b1c196b20 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Wed, 23 Jun 2021 01:35:27 +0800 Subject: [PATCH 08/23] feat(configure-mobile.client.directive): added loading state for toggle --- .../modules/forms/admin/css/edit-form.css | 6 +++++ .../configure-mobile.client.view.html | 4 +++ .../configure-mobile.client.directive.js | 25 ++++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/public/modules/forms/admin/css/edit-form.css b/src/public/modules/forms/admin/css/edit-form.css index ab84336a1d..25c1fa0882 100644 --- a/src/public/modules/forms/admin/css/edit-form.css +++ b/src/public/modules/forms/admin/css/edit-form.css @@ -1431,3 +1431,9 @@ a.modal-cancel-btn:hover { /* margin is set so that the error message appears to be in the same block as the toggle options */ margin: 0 15px; } + +/* Spinner */ +.loading-spinner { + display: flex; + justify-content: flex-end; +} diff --git a/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html b/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html index 84fe5b100f..f2d6d6f7c7 100644 --- a/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html +++ b/src/public/modules/forms/admin/directiveViews/configure-mobile.client.view.html @@ -12,7 +12,11 @@ >
+
+ +

-
+
You have reached the free tier limit for SMS verification. To continue From 294024a6cc72b6de7496ccb531c4165ed8d0d561 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Wed, 30 Jun 2021 15:28:47 +0800 Subject: [PATCH 11/23] feat(public/services): adds new smsservice --- src/public/services/SmsService.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/public/services/SmsService.ts diff --git a/src/public/services/SmsService.ts b/src/public/services/SmsService.ts new file mode 100644 index 0000000000..ae87ecdd8e --- /dev/null +++ b/src/public/services/SmsService.ts @@ -0,0 +1,19 @@ +import axios from 'axios' + +import { SmsCountsDto } from '../../types/api' + +/** Exported for testing */ +export const FORM_API_PREFIX = '/api/v3' +export const SMS_ENDPOINT = 'sms' + +/** + * Retrieves the sms verifications metadata from the backend for the admin of a specified form + * @param formId + * @returns + */ +export const getSmsVerificationStateForFormAdmin = ( + formId: string, +): Promise => + axios + .get(`${FORM_API_PREFIX}/${SMS_ENDPOINT}/${formId}`) + .then(({ data }) => data) From 985c1d6306d33dd0fcc1ae5dfbc3b349bc79a617 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Wed, 30 Jun 2021 15:51:51 +0800 Subject: [PATCH 12/23] feat(configure-mobile.client.directive): connected sms counts to fetch from backend --- .../configure-mobile.client.directive.js | 43 ++++++++++--------- .../admin/views/edit-fields.client.modal.html | 1 + 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js b/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js index aff9935cb3..9b8bf9436b 100644 --- a/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js +++ b/src/public/modules/forms/admin/directives/configure-mobile.client.directive.js @@ -1,10 +1,13 @@ 'use strict' +const { get } = require('lodash') const { ADMIN_VERIFIED_SMS_STATES, SMS_VERIFICATION_LIMIT, } = require('../../../../../shared/util/verification') +const SmsService = require('../../../../services/SmsService') + angular .module('forms') .directive('configureMobileDirective', [configureMobileDirective]) @@ -16,6 +19,7 @@ function configureMobileDirective() { restrict: 'E', scope: { field: '<', + form: '<', name: '=', characterLimit: '=', isLoading: '<', @@ -25,22 +29,13 @@ function configureMobileDirective() { '$uibModal', '$scope', '$translate', - '$timeout', - function ($q, $uibModal, $scope, $translate, $timeout) { + 'Toastr', + function ($q, $uibModal, $scope, $translate, Toastr) { // Get support form link from translation json. $translate('LINKS.TWILIO_SETUP_LINK').then((twilioSetupLink) => { $scope.twilioSetupLink = twilioSetupLink }) - // TODO: Implement this - const getSmsCounts = () => - $timeout(() => { - return { - verifiedSmsCount: 20000035, - messageServiceId: '', - } - }, 1000) - // NOTE: This is set on scope as it is used by the UI to determine if the toggle is loading $scope.isLoading = true @@ -54,21 +49,29 @@ function configureMobileDirective() { return ADMIN_VERIFIED_SMS_STATES.limitExceeded } - $q.when(getSmsCounts()).then( - ({ verifiedSmsCount, messageServiceId }) => { - $scope.verifiedSmsCount = verifiedSmsCount - $scope.messageServiceId = messageServiceId + $q.when(SmsService.getSmsVerificationStateForFormAdmin($scope.form._id)) + .then(({ msgSrvcSid, freeSmsCount }) => { + $scope.verifiedSmsCount = freeSmsCount + $scope.messageServiceId = msgSrvcSid $scope.adminVerifiedSmsState = getAdminVerifiedSmsState( - verifiedSmsCount, - messageServiceId, + freeSmsCount, + msgSrvcSid, ) // NOTE: This links into the verifiable field component and hence, is used by both email and mobile $scope.field.hasAdminExceededSmsLimit = $scope.adminVerifiedSmsState === ADMIN_VERIFIED_SMS_STATES.limitExceeded - $scope.isLoading = false - }, - ) + }) + .catch((error) => { + Toastr.error( + get( + error, + 'response.data.message', + 'Sorry, an error occurred. Please refresh the page and try again later.', + ), + ) + }) + .finally(() => ($scope.isLoading = false)) // Only open if the admin has sms counts below the limit. // If the admin has counts above limit without a message id, the toggle should be disabled anyway. diff --git a/src/public/modules/forms/admin/views/edit-fields.client.modal.html b/src/public/modules/forms/admin/views/edit-fields.client.modal.html index 98f1c99808..6dfd863430 100644 --- a/src/public/modules/forms/admin/views/edit-fields.client.modal.html +++ b/src/public/modules/forms/admin/views/edit-fields.client.modal.html @@ -849,6 +849,7 @@