feat(medusa-plugin-mailchimp): Migrate to TS + add update endpoint (#4040)

* - Refactored the .js files to .ts files
- Added typing
- Used 'WrapHandler' util instead of specific middleware in plugin
- Added additional PUT method to the /mailchimp/subscribe endpoint.
- Updated documentation where relevant.

* Added changeset

* Updated yarn.lock

* Used camelcase for method args in services/mailchimp.ts

---------

Co-authored-by: Tom Rupke <tom@adapptive.nl>
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2023-05-23 13:33:56 +02:00
1 parent 5c01b4e6b6
commit ddc90db530
19 files changed
+401 -158

No files matched your search

@@ -1,7 +1,7 @@
import { Router } from "express"
import routes from "./routes"
export default (rootDirectory) => {
export default function (rootDirectory: string): Router | Router[] {
const app = Router()
routes(app, rootDirectory)
@@ -1 +0,0 @@
export default (fn) => (...args) => fn(...args).catch(args[2])
@@ -1,5 +0,0 @@
import { default as wrap } from "./await-middleware"
export default {
wrap,
}
@@ -1,30 +0,0 @@
import { Router } from "express"
import bodyParser from "body-parser"
import middlewares from "../middleware"
import { getConfigFile, parseCorsOrigins } from "medusa-core-utils"
import cors from "cors"
const route = Router()
export default (app, rootDirectory) => {
const { configModule } = getConfigFile(rootDirectory, `medusa-config`)
const config = (configModule && configModule.projectConfig) || {}
const storeCors = config.store_cors || ""
route.use(
cors({
origin: parseCorsOrigins(storeCors),
credentials: true,
})
)
app.use("/mailchimp", route)
route.post(
"/subscribe",
bodyParser.json(),
middlewares.wrap(require("./subscribe-newsletter").default)
)
return app
}
@@ -0,0 +1,39 @@
import { Router } from "express"
import * as cors from "cors"
import * as bodyParser from "body-parser"
import configLoader from "@medusajs/medusa/dist/loaders/config"
import { parseCorsOrigins } from "medusa-core-utils"
import { wrapHandler } from "@medusajs/medusa"
import {
update as subscribeNewsletterUpdate,
add as subscribeNewsletterAdd,
} from "./subscribe-newsletter"
const router = Router()
export default (app: Router, rootDirectory) => {
const config = configLoader(rootDirectory)
const corsOptions = {
origin: parseCorsOrigins(config.projectConfig.store_cors || ""),
credentials: true,
}
app.use("/mailchimp", router)
router.use(cors(corsOptions))
router.post(
"/subscribe",
bodyParser.json(),
wrapHandler(subscribeNewsletterAdd)
)
router.put(
"/subscribe",
bodyParser.json(),
wrapHandler(subscribeNewsletterUpdate)
)
return app
}
@@ -1,8 +0,0 @@
export default async (req, res) => {
const mailchimpService = req.scope.resolve("mailchimpService")
await mailchimpService.subscribeNewsletter(
req.body.email,
req.body.data || {}
)
res.sendStatus(200)
}
@@ -0,0 +1,19 @@
import { Request, Response } from "express"
export async function add(req: Request, res: Response) {
const mailchimpService = req.scope.resolve("mailchimpService")
await mailchimpService.subscribeNewsletterAdd(
req.body.email,
req.body.data || {}
)
res.sendStatus(200)
}
export async function update(req: Request, res: Response) {
const mailchimpService = req.scope.resolve("mailchimpService")
await mailchimpService.subscribeNewsletterUpdate(
req.body.email,
req.body.data || {}
)
res.sendStatus(200)
}
@@ -1,21 +0,0 @@
const MailchimpMock = jest.fn().mockImplementation(() => {
return {
subscribeNewsletter: jest.fn().mockReturnValue(Promise.resolve("success")),
}
})
describe("MailchimpService", () => {
describe("newsletterSubscribe", () => {
let result
beforeAll(async () => {
jest.clearAllMocks()
const mailchimpService = new MailchimpMock()
result = await mailchimpService.subscribeNewsletter("medusa@medusa.com")
})
it("resolves successfully", () => {
expect(result).toEqual("success")
})
})
})
@@ -0,0 +1,43 @@
const MailchimpMock = jest.fn().mockImplementation(() => {
return {
subscribeNewsletterAdd: jest
.fn()
.mockReturnValue(Promise.resolve("success")),
subscribeNewsletterUpdate: jest
.fn()
.mockReturnValue(Promise.resolve("success")),
}
})
describe("MailchimpService", () => {
describe("newsletterSubscribe", () => {
let result
beforeAll(async () => {
jest.clearAllMocks()
const mailchimpService = new MailchimpMock()
result = await mailchimpService.subscribeNewsletterAdd(
"medusa@medusa.com"
)
})
it("resolves successfully", () => {
expect(result).toEqual("success")
})
}),
describe("newsletterSubscribeUpdate", () => {
let result
beforeAll(async () => {
jest.clearAllMocks()
const mailchimpService = new MailchimpMock()
result = await mailchimpService.subscribeNewsletterUpdate(
"medusa@medusa.com"
)
})
it("resolves successfully", () => {
expect(result).toEqual("success")
})
})
})
@@ -1,39 +0,0 @@
import { BaseService } from "medusa-interfaces"
import Mailchimp from "mailchimp-api-v3"
class MailchimpService extends BaseService {
/**
* @param {Object} options - options defined in `medusa-config.js`
* e.g.
* {
* api_key: Mailchimp api key
* newsletter_list_id: "123456789"
* }
*/
constructor({}, options) {
super()
this.options_ = options
this.mailchimp_ = new Mailchimp(options.api_key)
}
/**
* Subscribes an email to a newsletter.
* @param {string} email - email to use for the subscription
* @param {Object} data - additional data (see https://mailchimp.com/developer/api/marketing/list-members/add-member-to-list/)
* @return {Promise} result of newsletter subscription
*/
async subscribeNewsletter(email, data) {
return this.mailchimp_.post(
`/lists/${this.options_.newsletter_list_id}/members`,
{
email_address: email,
status: "subscribed",
...data,
}
)
}
}
export default MailchimpService
@@ -0,0 +1,71 @@
import { TransactionBaseService } from "@medusajs/medusa"
import Mailchimp = require("mailchimp-api-v3")
import * as crypto from "crypto"
import { MailchimpPluginOptions } from "../types"
class MailchimpService extends TransactionBaseService {
protected options_: MailchimpPluginOptions
protected mailchimp_: Mailchimp
/**
* @param {Object} options - options defined in `medusa-config.js`
* e.g.
* {
* api_key: Mailchimp api key
* newsletter_list_id: "123456789"
* }
*/
constructor(_, options: MailchimpPluginOptions) {
super(_, options)
this.options_ = options
this.mailchimp_ = new Mailchimp(options.api_key)
}
/**
* Subscribes an email to a newsletter.
* @param {string} email - email to use for the subscription
* @param {Object} data - additional data (see https://mailchimp.com/developer/marketing/api/list-merges/)
* @return {Promise} result of newsletter subscription
*/
async subscribeNewsletterAdd(email: string, data: any) {
return this.mailchimp_.post(
`/lists/${this.options_.newsletter_list_id}/members`,
{
email_address: email,
status: "subscribed",
...data,
}
)
}
/**
* Updates an email to a newsletter.
* @param {string} email - email to use for the subscription
* @param {Object} data - additional data (see https://mailchimp.com/developer/marketing/api/list-merges/)
* @return {Promise} result of newsletter subscription
*/
async subscribeNewsletterUpdate(
email: string,
data: any,
statusIfNew?: string,
status?: string
) {
const lowercase = email.toLowerCase()
const hash = crypto.createHash("md5").update(lowercase).digest("hex")
return this.mailchimp_.put(
`/lists/${this.options_.newsletter_list_id}/members/${hash}`,
{
email_address: email,
status_if_new: statusIfNew || "subscribed",
status: status || "subscribed",
...data,
}
)
}
}
export default MailchimpService
@@ -0,0 +1,4 @@
export type MailchimpPluginOptions = {
newsletter_list_id: string
api_key: string
}