From d5bb3b9923322a3d67e073df11076e0e6acd237c Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Fri, 16 Jun 2023 13:34:04 +0100 Subject: [PATCH] feat(server): mailchimp plumbing --- .../server/modules/auth/services/mailchimp.ts | 44 +++++++++++++++++-- packages/server/modules/auth/strategies.js | 4 +- .../modules/shared/helpers/envHelper.ts | 16 +++++++ packages/server/package.json | 2 + yarn.lock | 39 +++++++++++++++- 5 files changed, 98 insertions(+), 7 deletions(-) diff --git a/packages/server/modules/auth/services/mailchimp.ts b/packages/server/modules/auth/services/mailchimp.ts index bef3c3c21..77a7bd7d4 100644 --- a/packages/server/modules/auth/services/mailchimp.ts +++ b/packages/server/modules/auth/services/mailchimp.ts @@ -1,8 +1,46 @@ +/* eslint-disable camelcase */ +import mailchimp from '@mailchimp/mailchimp_marketing' import { logger } from '@/logging/logging' +import { md5 } from '@/modules/shared/helpers/cryptoHelper' +import { getMailchimpConfig } from '@/modules/shared/helpers/envHelper' +import { getUserById } from '@/modules/core/services/users' -function addToMailchimpAudience(email: string) { - // TODO - console.log('yes, we shall chimp the mail') +// Note: fails here should not block registration at any cost +async function addToMailchimpAudience(userId: string) { + try { + const config = getMailchimpConfig() // Note: throws an error if not configured + + mailchimp.setConfig({ + apiKey: config.apiKey, + server: config.serverPrefix + }) + + const user = await getUserById({ userId }) + + if (!user) { + throw new Error( + 'Could not register user for newsletter - no db user record found.' + ) + } + + const [first, second] = user.name.split(' ') + const subscriberHash = md5(user.email.toLowerCase()) + + // NOTE: using setListMember (NOT addListMember) to prevent errors for previously + // registered members. + await mailchimp.lists.setListMember(config.listId + 'fail', subscriberHash, { + status_if_new: 'subscribed', + email_address: user.email, + merge_fields: { + EMAIL: user.email, + FNAME: first, + LNAME: second, + FULLNAME: user.name // NOTE: this field needs to be set in the audience merge fields + } + }) + } catch (e) { + logger.warn(e, 'Failed to register user to newsletter.') + } } export { addToMailchimpAudience } diff --git a/packages/server/modules/auth/strategies.js b/packages/server/modules/auth/strategies.js index 85b730179..47195b4c7 100644 --- a/packages/server/modules/auth/strategies.js +++ b/packages/server/modules/auth/strategies.js @@ -93,9 +93,7 @@ module.exports = async (app) => { } if (newsletterConsent) { - // TODO send out mailchimp stuff - console.log('YOLO WE WANT NEWS', req.user.email) - addToMailchimpAudience(req.user.email) + await addToMailchimpAudience(req.user.id) } const redirectUrl = urlObj.toString() diff --git a/packages/server/modules/shared/helpers/envHelper.ts b/packages/server/modules/shared/helpers/envHelper.ts index a3c6982c0..acfecca12 100644 --- a/packages/server/modules/shared/helpers/envHelper.ts +++ b/packages/server/modules/shared/helpers/envHelper.ts @@ -81,6 +81,22 @@ export function getOidcName() { return process.env.OIDC_NAME } +export function getMailchimpConfig() { + if ( + !process.env.MAILCHIMP_API_KEY || + !process.env.MAILCHIMP_SERVER_PREFIX || + !process.env.MAILCHIMP_LIST_ID + ) { + throw new MisconfiguredEnvironmentError('Mailchimp is not configured') + } + + return { + apiKey: process.env.MAILCHIMP_API_KEY, + serverPrefix: process.env.MAILCHIMP_SERVER_PREFIX, + listId: process.env.MAILCHIMP_LIST_ID + } +} + /** * Get app base url / canonical url / origin * TODO: Go over all getBaseUrl() usages and move them to getXOrigin() instead diff --git a/packages/server/package.json b/packages/server/package.json index d5fc5f7f5..0015a2e7b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -36,9 +36,11 @@ "@aws-sdk/lib-storage": "^3.100.0", "@godaddy/terminus": "^4.9.0", "@graphql-tools/schema": "^9.0.4", + "@mailchimp/mailchimp_marketing": "^3.0.80", "@sentry/node": "^6.17.9", "@sentry/tracing": "^6.17.9", "@speckle/shared": "workspace:^", + "@types/mailchimp__mailchimp_marketing": "^3.0.9", "@types/pino-http": "^5.8.1", "@types/uuid": "^9.0.0", "apollo-server-express": "^3.10.2", diff --git a/yarn.lock b/yarn.lock index a3b900e9b..b3d9a345a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9348,6 +9348,16 @@ __metadata: languageName: node linkType: hard +"@mailchimp/mailchimp_marketing@npm:^3.0.80": + version: 3.0.80 + resolution: "@mailchimp/mailchimp_marketing@npm:3.0.80" + dependencies: + dotenv: ^8.2.0 + superagent: 3.8.1 + checksum: a43f69334766bf02fccf4f6076fa38b7e6a53d9d99485547e1e11d23f9188349ac2831a329f15bb8e31be46cca5e22275a120d0d108b80212ed92ad12df709e4 + languageName: node + linkType: hard + "@mapbox/node-pre-gyp@npm:^1.0.0": version: 1.0.9 resolution: "@mapbox/node-pre-gyp@npm:1.0.9" @@ -11229,6 +11239,7 @@ __metadata: "@graphql-codegen/typescript-operations": ^2.5.2 "@graphql-codegen/typescript-resolvers": 2.7.2 "@graphql-tools/schema": ^9.0.4 + "@mailchimp/mailchimp_marketing": ^3.0.80 "@sentry/node": ^6.17.9 "@sentry/tracing": ^6.17.9 "@speckle/objectloader": "workspace:^" @@ -11243,6 +11254,7 @@ __metadata: "@types/ejs": ^3.1.1 "@types/express": ^4.17.13 "@types/lodash": ^4.14.180 + "@types/mailchimp__mailchimp_marketing": ^3.0.9 "@types/mjml": ^4.7.0 "@types/mocha": ^10.0.0 "@types/mock-require": ^2.0.1 @@ -15157,6 +15169,13 @@ __metadata: languageName: node linkType: hard +"@types/mailchimp__mailchimp_marketing@npm:^3.0.9": + version: 3.0.9 + resolution: "@types/mailchimp__mailchimp_marketing@npm:3.0.9" + checksum: 3bf413367a77a331fd87552096ce42f23baee3f34309c91766d26095bc686fa541aa1ce5d72061fd66e9e2b8bb3589542b4709e04043c1ca54e28e71e4480934 + languageName: node + linkType: hard + "@types/mdx@npm:^2.0.0": version: 2.0.3 resolution: "@types/mdx@npm:2.0.3" @@ -25476,7 +25495,7 @@ __metadata: languageName: node linkType: hard -"formidable@npm:^1.2.0": +"formidable@npm:^1.1.1, formidable@npm:^1.2.0": version: 1.2.6 resolution: "formidable@npm:1.2.6" checksum: 2b68ed07ba88302b9c63f8eda94f19a460cef6017bfda48348f09f41d2a36660c9353137991618e0e4c3db115b41e4b8f6fa63bc973b7a7c91dec66acdd02a56 @@ -40390,6 +40409,24 @@ __metadata: languageName: node linkType: hard +"superagent@npm:3.8.1": + version: 3.8.1 + resolution: "superagent@npm:3.8.1" + dependencies: + component-emitter: ^1.2.0 + cookiejar: ^2.1.0 + debug: ^3.1.0 + extend: ^3.0.0 + form-data: ^2.3.1 + formidable: ^1.1.1 + methods: ^1.1.1 + mime: ^1.4.1 + qs: ^6.5.1 + readable-stream: ^2.0.5 + checksum: 42895e220fb5aab303edeef7ec4d9c38fef31638d18254fe57329366e7a74624dffe20acd682a9a27df4f62fe4acb953b33d16be9611f184284815c2492d35cf + languageName: node + linkType: hard + "superagent@npm:^3.7.0, superagent@npm:^3.8.3": version: 3.8.3 resolution: "superagent@npm:3.8.3"