Skip to content

Commit 3f7ba04

Browse files
committed
chore: remove growthbook from payment functions
1 parent 7fafaec commit 3f7ba04

File tree

8 files changed

+232
-55
lines changed

8 files changed

+232
-55
lines changed

src/app/modules/payments/payments.service.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { GrowthBook } from '@growthbook/growthbook'
21
import { isEqual, omit } from 'lodash'
32
import moment from 'moment-timezone'
43
import mongoose, { Types } from 'mongoose'
@@ -209,7 +208,6 @@ export const confirmPaymentPendingSubmission = (
209208
*/
210209
export const performPaymentPostSubmissionActions = (
211210
paymentId: IPaymentSchema['_id'],
212-
growthbook: GrowthBook | undefined,
213211
): ResultAsync<
214212
void,
215213
| PaymentNotFoundError
@@ -244,7 +242,6 @@ export const performPaymentPostSubmissionActions = (
244242
performEncryptPostSubmissionActions({
245243
submission,
246244
responses: payment.responses,
247-
growthbook,
248245
})
249246
.andThen(() =>
250247
// If successfully sent email confirmations, delete response data from payment document.

src/app/modules/payments/stripe.controller.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,7 @@ export const reconcileAccount: ControllerHandler<
304304
meta: { ...logMeta, event },
305305
})
306306

307-
await StripeService.handleStripeEvent(
308-
event as Stripe.DiscriminatedEvent,
309-
req.growthbook,
310-
)
307+
await StripeService.handleStripeEvent(event as Stripe.DiscriminatedEvent)
311308
.andThen(() => {
312309
logger.warn({
313310
message:

src/app/modules/payments/stripe.events.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const _handleStripeEventUpdates: ControllerHandler<
8989

9090
// Step 3: Process the event
9191
return (
92-
StripeService.handleStripeEvent(event, req.growthbook)
92+
StripeService.handleStripeEvent(event)
9393
// Step 4: Return response to Stripe based on result
9494
.match(
9595
() => res.sendStatus(StatusCodes.OK),

src/app/modules/payments/stripe.service.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Use 'stripe-event-types' for better type discrimination.
22
/// <reference types="stripe-event-types" />
3-
import { GrowthBook } from '@growthbook/growthbook'
43
import cuid from 'cuid'
54
import mongoose from 'mongoose'
65
import { errAsync, ok, okAsync, ResultAsync } from 'neverthrow'
@@ -451,7 +450,6 @@ type HandleStripeEventResultError =
451450
*/
452451
export const handleStripeEvent = (
453452
event: Stripe.DiscriminatedEvent,
454-
growthbook: GrowthBook | undefined,
455453
): ResultAsync<void, HandleStripeEventResultError> => {
456454
const logMeta = {
457455
action: 'handleStripeEvent',
@@ -498,7 +496,6 @@ export const handleStripeEvent = (
498496

499497
return PaymentsService.performPaymentPostSubmissionActions(
500498
paymentId,
501-
growthbook,
502499
)
503500
.andThen(() => okAsync(undefined))
504501
.orElse((e) => {

src/app/modules/submission/multirespondent-submission/multirespondent-submission.service.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import {
4343
AutoReplyMailData,
4444
AutoreplySummaryRenderData,
4545
} from '../../../services/mail/mail.types'
46-
import { generateAutoreplyPdf } from '../../../services/mail/mail.utils'
46+
import { AutoReplyData, generateAutoreplyPdf } from '../../../services/mail/mail.utils'
4747
import { transformMongoError } from '../../../utils/handle-mongo-error'
4848
import { DatabaseError } from '../../core/core.errors'
4949
import { isFormMultirespondent } from '../../form/form.utils'
@@ -486,11 +486,6 @@ const sendMrfRespondentCopyEmails = ({
486486
InvalidWorkflowTypeError | MailSendError | AutoreplyPdfGenerationError
487487
> => {
488488
const submissionId: string = submission.id
489-
const submissionTime = moment(submission.created)
490-
.tz('Asia/Singapore')
491-
.format('ddd, DD MMM YYYY hh:mm:ss A')
492-
const formUrl: string = `${config.app.appUrl}/${form._id}`
493-
494489
const formQuestionAnswers = getQuestionTitleAnswerString({
495490
formFields: form.form_fields,
496491
responses,
@@ -504,20 +499,21 @@ const sendMrfRespondentCopyEmails = ({
504499
const hasFormSummary = respondentCopyRecipientData.some(
505500
(autoReplyMailData) => autoReplyMailData.includeFormSummary,
506501
)
507-
const renderData: AutoreplySummaryRenderData = {
502+
503+
const autoReplyData: AutoReplyData = {
508504
refNo: submissionId,
509505
formTitle: form.title,
510-
submissionTime: submissionTime,
511-
formData: pdfFormData,
512-
formUrl: formUrl,
506+
submissionDateTime: submission.created || new Date(),
507+
responsesData: pdfFormData,
508+
formUrl: `${config.app.appUrl}/${form._id}`
513509
}
514510

515511
// Step 1: generate PDF if needed
516512
const pdfResult: ResultAsync<
517513
Mail.Attachment | undefined,
518514
AutoreplyPdfGenerationError
519515
> = hasFormSummary
520-
? generateAutoreplyPdf(renderData, true).map((pdfBuffer) => ({
516+
? generateAutoreplyPdf(autoReplyData, true).map((pdfBuffer) => ({
521517
filename: 'response.pdf',
522518
content: Buffer.copyBytesFrom(pdfBuffer),
523519
}))
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { ObjectId } from 'bson'
2+
import { BasicField } from 'shared/types'
3+
import { SIGNATURE_CAPTURED_STRING } from 'shared/utils/signature'
4+
5+
import * as ConvertHtmlToPdf from '../../../utils/convert-html-to-pdf'
6+
import { generateAutoreplyPdf, safeRenderFileForTest } from '../mail.utils'
7+
8+
jest.mock('../../../utils/convert-html-to-pdf.ts')
9+
const MockConvertHtmlToPdf = jest.mocked(ConvertHtmlToPdf)
10+
11+
const MOCK_PDF_BUFFER = Buffer.from('mock pdf buffer')
12+
const MOCK_SUBMISSION_DATE_TIME = new Date('2025-01-01T00:00:00.000Z')
13+
14+
describe('mail.utils', () => {
15+
const AUTOREPLY_PDF_TEMPLATE_FILEPATH = `${__dirname}/../../../views/templates/submit-form-summary-pdf.server.view.html`
16+
beforeEach(() => {
17+
jest.clearAllMocks()
18+
MockConvertHtmlToPdf.generatePdfFromHtml.mockResolvedValue(MOCK_PDF_BUFFER)
19+
})
20+
21+
describe('generateAutoreplyPdf', () => {
22+
it('should generate a pdf from html', async () => {
23+
// Arrange
24+
const mockFormId = new ObjectId().toHexString()
25+
const mockSubmissionId = new ObjectId().toHexString()
26+
const autoReplyData = {
27+
refNo: mockSubmissionId,
28+
formTitle: 'Test Form',
29+
submissionDateTime: MOCK_SUBMISSION_DATE_TIME,
30+
responsesData: [],
31+
formUrl: `https://form.gov.sg/${mockFormId}`,
32+
}
33+
34+
// Act
35+
const result = await generateAutoreplyPdf(autoReplyData, true)
36+
37+
// Assert
38+
expect(MockConvertHtmlToPdf.generatePdfFromHtml).toHaveBeenCalledOnce()
39+
expect(result._unsafeUnwrap()).toEqual(MOCK_PDF_BUFFER)
40+
})
41+
42+
it('should generate pdfRender data correctly for signature field', async () => {
43+
// Arrange
44+
const mockFormId = new ObjectId().toHexString()
45+
const mockSubmissionId = new ObjectId().toHexString()
46+
const MOCK_SIGNATURE_PNG_DATAURI = 'datauri://signature.png'
47+
const autoReplyData = {
48+
refNo: mockSubmissionId,
49+
formTitle: 'Test Form',
50+
submissionDateTime: MOCK_SUBMISSION_DATE_TIME,
51+
responsesData: [
52+
{
53+
question: 'Signature',
54+
answer: MOCK_SIGNATURE_PNG_DATAURI,
55+
answerTemplate: [SIGNATURE_CAPTURED_STRING],
56+
fieldType: BasicField.Signature,
57+
},
58+
],
59+
formUrl: `https://form.gov.sg/${mockFormId}`,
60+
}
61+
62+
const formattedSubmissionTimeString = 'Wed, 01 Jan 2025 08:00:00 AM'
63+
64+
const pdfRenderData = {
65+
refNo: mockSubmissionId,
66+
formTitle: 'Test Form',
67+
submissionTime: formattedSubmissionTimeString,
68+
formData: [
69+
{
70+
question: 'Signature',
71+
answer: MOCK_SIGNATURE_PNG_DATAURI, // should be defined for signature fields
72+
answerTemplate: [SIGNATURE_CAPTURED_STRING],
73+
},
74+
],
75+
formUrl: `https://form.gov.sg/${mockFormId}`,
76+
}
77+
78+
const expectedHtml = await safeRenderFileForTest(
79+
AUTOREPLY_PDF_TEMPLATE_FILEPATH,
80+
pdfRenderData,
81+
)
82+
83+
// Act
84+
const result = await generateAutoreplyPdf(autoReplyData, true)
85+
86+
// Assert
87+
expect(MockConvertHtmlToPdf.generatePdfFromHtml).toHaveBeenCalledOnce()
88+
expect(MockConvertHtmlToPdf.generatePdfFromHtml).toHaveBeenCalledWith(
89+
expectedHtml._unsafeUnwrap(),
90+
true,
91+
)
92+
expect(result._unsafeUnwrap()).toEqual(MOCK_PDF_BUFFER)
93+
})
94+
95+
it('should generate pdfRender data correctly for non-signature fields', async () => {
96+
// Arrange
97+
const mockFormId = new ObjectId().toHexString()
98+
const mockSubmissionId = new ObjectId().toHexString()
99+
const autoReplyData = {
100+
refNo: mockSubmissionId,
101+
formTitle: 'Test Form',
102+
submissionDateTime: MOCK_SUBMISSION_DATE_TIME,
103+
responsesData: [
104+
{
105+
question: 'Table field',
106+
answer: 'Table answer',
107+
answerTemplate: ['Table answer'],
108+
fieldType: BasicField.Table,
109+
},
110+
],
111+
formUrl: `https://form.gov.sg/${mockFormId}`,
112+
}
113+
114+
const formattedSubmissionTimeString = 'Wed, 01 Jan 2025 08:00:00 AM'
115+
116+
const pdfRenderData = {
117+
refNo: mockSubmissionId,
118+
formTitle: 'Test Form',
119+
submissionTime: formattedSubmissionTimeString,
120+
formData: [
121+
{
122+
question: 'Table field',
123+
answer: undefined, // should be undefined for non-signature fields
124+
answerTemplate: ['Table answer'],
125+
},
126+
],
127+
formUrl: `https://form.gov.sg/${mockFormId}`,
128+
}
129+
const expectedHtml = await safeRenderFileForTest(
130+
AUTOREPLY_PDF_TEMPLATE_FILEPATH,
131+
pdfRenderData,
132+
)
133+
134+
// Act
135+
const result = await generateAutoreplyPdf(autoReplyData, true)
136+
137+
// Assert
138+
expect(MockConvertHtmlToPdf.generatePdfFromHtml).toHaveBeenCalledOnce()
139+
expect(MockConvertHtmlToPdf.generatePdfFromHtml).toHaveBeenCalledWith(
140+
expectedHtml._unsafeUnwrap(),
141+
true,
142+
)
143+
expect(result._unsafeUnwrap()).toEqual(MOCK_PDF_BUFFER)
144+
})
145+
})
146+
})

src/app/services/mail/mail.service.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import {
6262
SubmissionToAdminHtmlData,
6363
} from './mail.types'
6464
import {
65+
AutoReplyData,
6566
generateAutoreplyHtml,
6667
generateAutoreplyPdf,
6768
generateIssueReportedNotificationHtml,
@@ -796,18 +797,6 @@ export class MailService {
796797
>
797798
>[]
798799
> => {
799-
// Data to render both the submission details mail HTML body and PDF.
800-
801-
const renderData: AutoreplySummaryRenderData = {
802-
refNo: submission.id,
803-
formTitle: form.title,
804-
submissionTime: moment(submission.created)
805-
.tz('Asia/Singapore')
806-
.format('ddd, DD MMM YYYY hh:mm:ss A'),
807-
formData: responsesData,
808-
formUrl: `${this.#appUrl}/${form._id}`,
809-
}
810-
811800
// Create a copy of attachments for attaching of autoreply pdf if needed.
812801
const attachmentsWithAutoreplyPdf = [...attachments]
813802
const isEncryptForm = form?.responseMode === FormResponseMode.Encrypt
@@ -817,14 +806,21 @@ export class MailService {
817806
encryptFormDef.payments_channel.channel !== PaymentChannel.Unconnected &&
818807
encryptFormDef.payments_field.enabled === true
819808

809+
const autoReplyData: AutoReplyData = {
810+
refNo: submission.id,
811+
formTitle: form.title,
812+
submissionDateTime: submission.created || new Date(),
813+
responsesData,
814+
formUrl: `${this.#appUrl}/${form._id}`,
815+
}
820816
// Generate autoreply pdf and append into attachments if any of the mail has
821817
// to include a form summary.
822818
if (
823819
autoReplyMailDatas.some((data) => data.includeFormSummary) &&
824820
!isPaymentEnabled
825821
) {
826822
const pdfBufferResult = await generateAutoreplyPdf(
827-
renderData,
823+
autoReplyData,
828824
isUseLambdaOutput,
829825
)
830826
if (pdfBufferResult.isErr()) {
@@ -844,9 +840,14 @@ export class MailService {
844840
}),
845841
)
846842

847-
const strippedRenderData = {
848-
...renderData,
843+
const strippedRenderData: AutoreplySummaryRenderData = {
844+
refNo: submission.id,
845+
formTitle: form.title,
846+
submissionTime: moment(submission.created)
847+
.tz('Asia/Singapore')
848+
.format('ddd, DD MMM YYYY hh:mm:ss A'),
849849
formData: strippedResponsesData,
850+
formUrl: `${this.#appUrl}/${form._id}`,
850851
}
851852

852853
// Prepare mail sending for each autoreply mail.

0 commit comments

Comments
 (0)