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:
19 files changed
+401
-158
No files matched your search
@@ -22,32 +22,31 @@ Manage newsletter subscriptions in your commerce application with Mailchimp.
|
||||
|
||||
1\. Run the following command in the directory of the Medusa backend:
|
||||
|
||||
```bash
|
||||
npm install medusa-plugin-mailchimp
|
||||
```
|
||||
```bash
|
||||
npm install medusa-plugin-mailchimp
|
||||
```
|
||||
|
||||
2\. Set the following environment variables in `.env`:
|
||||
|
||||
```bash
|
||||
MAILCHIMP_API_KEY=<YOUR_API_KEY>
|
||||
MAILCHIMP_NEWSLETTER_LIST_ID=<YOUR_NEWSLETTER_LIST_ID>
|
||||
```
|
||||
```bash
|
||||
MAILCHIMP_API_KEY=<YOUR_API_KEY>
|
||||
MAILCHIMP_NEWSLETTER_LIST_ID=<YOUR_NEWSLETTER_LIST_ID>
|
||||
```
|
||||
|
||||
3\. In `medusa-config.js` add the following at the end of the `plugins` array:
|
||||
|
||||
```js
|
||||
const plugins = [
|
||||
// ...,
|
||||
{
|
||||
resolve: `medusa-plugin-mailchimp`,
|
||||
options: {
|
||||
api_key: process.env.MAILCHIMP_API_KEY,
|
||||
newsletter_list_id:
|
||||
process.env.MAILCHIMP_NEWSLETTER_LIST_ID,
|
||||
},
|
||||
```js
|
||||
const plugins = [
|
||||
// ...,
|
||||
{
|
||||
resolve: `medusa-plugin-mailchimp`,
|
||||
options: {
|
||||
api_key: process.env.MAILCHIMP_API_KEY,
|
||||
newsletter_list_id: process.env.MAILCHIMP_NEWSLETTER_LIST_ID,
|
||||
},
|
||||
]
|
||||
```
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -55,11 +54,11 @@ Manage newsletter subscriptions in your commerce application with Mailchimp.
|
||||
|
||||
1\. Run the following command in the directory of the Medusa backend to run the backend:
|
||||
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
|
||||
2\. Use the `/mailchimp/subscribe` endpoint or the `MailchimpService` to subscribe to the newsletter.
|
||||
2\. Use the POST or PUT `/mailchimp/subscribe` endpoint or the `MailchimpService` to subscribe to the newsletter.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -8,34 +8,26 @@
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
"directory": "packages/medusa-plugin-mailchimp"
|
||||
},
|
||||
"author": "Oliver Juhl",
|
||||
"author": "Oliver Juhl, Tomek Pur",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.7.5",
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/node": "^7.7.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.5",
|
||||
"@babel/register": "^7.7.4",
|
||||
"@babel/runtime": "^7.9.6",
|
||||
"@medusajs/medusa": "1.10.0",
|
||||
"@types/express": "^4.17.17",
|
||||
"client-sessions": "^0.8.0",
|
||||
"cross-env": "^5.2.1",
|
||||
"jest": "^25.5.4",
|
||||
"medusa-interfaces": "^1.3.7"
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "cross-env NODE_ENV=production yarn run build",
|
||||
"test": "jest --passWithNoTests src",
|
||||
"build": "babel src --out-dir . --ignore '**/__tests__','**/__mocks__'",
|
||||
"watch": "babel -w src --out-dir . --ignore '**/__tests__','**/__mocks__'"
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"medusa-interfaces": "1.3.7"
|
||||
"@medusajs/medusa": "1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-classes": "^7.9.5",
|
||||
"body-parser": "^1.19.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["es5", "es6"],
|
||||
"target": "esnext",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": false,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"declaration": false,
|
||||
"sourceMap": false,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "src",
|
||||
"baseUrl": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": [
|
||||
"dist",
|
||||
"./src/**/__tests__",
|
||||
"./src/**/__mocks__",
|
||||
"./src/**/__fixtures__",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["dist", "node_modules"]
|
||||
}
|
||||
Reference in new issue
Block a user