mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #18021 from overleaf/rh-mailchimp-api
[web] Replace node-mailchimp with own MailChimpClient GitOrigin-RevId: 10207620c48f30ad29f4f0e7ea5193c11d256902
This commit is contained in:
parent
9601fd097a
commit
06cac44d84
5 changed files with 78 additions and 140 deletions
131
package-lock.json
generated
131
package-lock.json
generated
|
@ -27908,78 +27908,6 @@
|
|||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mailchimp-api-v3": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/mailchimp-api-v3/-/mailchimp-api-v3-1.15.0.tgz",
|
||||
"integrity": "sha512-9TxCFG+VRpl14HOHgABHYmC5GvpCY7LYqyTefOXd4GtI07oXCiJ7W5fEvk3SJKBctlbjhKbzjB5qOZMQpacEUQ==",
|
||||
"dependencies": {
|
||||
"bluebird": "^3.4.0",
|
||||
"lodash": "^4.17.14",
|
||||
"request": "^2.88.0",
|
||||
"tar": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/mailchimp-api-v3/node_modules/fs-minipass": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||
"dependencies": {
|
||||
"minipass": "^2.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mailchimp-api-v3/node_modules/minipass": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mailchimp-api-v3/node_modules/minizlib": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||
"dependencies": {
|
||||
"minipass": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mailchimp-api-v3/node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/mailchimp-api-v3/node_modules/tar": {
|
||||
"version": "4.4.19",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
|
||||
"integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
|
||||
"dependencies": {
|
||||
"chownr": "^1.1.4",
|
||||
"fs-minipass": "^1.2.7",
|
||||
"minipass": "^2.9.0",
|
||||
"minizlib": "^1.3.3",
|
||||
"mkdirp": "^0.5.5",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"yallist": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.5"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||
|
@ -43585,7 +43513,6 @@
|
|||
"jsonwebtoken": "^9.0.0",
|
||||
"lodash": "^4.17.19",
|
||||
"lru-cache": "^7.10.1",
|
||||
"mailchimp-api-v3": "^1.12.0",
|
||||
"marked": "^4.1.0",
|
||||
"method-override": "^2.3.3",
|
||||
"minimatch": "^7.4.2",
|
||||
|
@ -52263,7 +52190,6 @@
|
|||
"less-loader": "^11.1.3",
|
||||
"lodash": "^4.17.19",
|
||||
"lru-cache": "^7.10.1",
|
||||
"mailchimp-api-v3": "^1.12.0",
|
||||
"marked": "^4.1.0",
|
||||
"match-sorter": "^6.2.0",
|
||||
"mathjax": "^3.2.2",
|
||||
|
@ -69281,63 +69207,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"mailchimp-api-v3": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/mailchimp-api-v3/-/mailchimp-api-v3-1.15.0.tgz",
|
||||
"integrity": "sha512-9TxCFG+VRpl14HOHgABHYmC5GvpCY7LYqyTefOXd4GtI07oXCiJ7W5fEvk3SJKBctlbjhKbzjB5qOZMQpacEUQ==",
|
||||
"requires": {
|
||||
"bluebird": "^3.4.0",
|
||||
"lodash": "^4.17.14",
|
||||
"request": "^2.88.0",
|
||||
"tar": "^4.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-minipass": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||
"requires": {
|
||||
"minipass": "^2.6.0"
|
||||
}
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||
"requires": {
|
||||
"minipass": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.19",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
|
||||
"integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.4",
|
||||
"fs-minipass": "^1.2.7",
|
||||
"minipass": "^2.9.0",
|
||||
"minizlib": "^1.3.3",
|
||||
"mkdirp": "^0.5.5",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"yallist": "^3.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"make-dir": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||
|
|
60
services/web/app/src/Features/Newsletter/MailChimpClient.js
Normal file
60
services/web/app/src/Features/Newsletter/MailChimpClient.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
const { fetchJson, fetchNothing } = require('@overleaf/fetch-utils')
|
||||
|
||||
class MailChimpClient {
|
||||
constructor(apiKey) {
|
||||
this.apiKey = apiKey
|
||||
this.dc = apiKey.split('-')[1]
|
||||
this.baseUrl = `https://${this.dc}.api.mailchimp.com/3.0/`
|
||||
this.fetchOptions = {
|
||||
method: 'GET',
|
||||
basicAuth: {
|
||||
user: 'any',
|
||||
password: this.apiKey,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async request(path, options) {
|
||||
try {
|
||||
const requestUrl = `${this.baseUrl}${path}`
|
||||
if (options.method === 'GET') {
|
||||
return await fetchJson(requestUrl, options)
|
||||
}
|
||||
await fetchNothing(requestUrl, options)
|
||||
} catch (err) {
|
||||
// if there's a json body in the response, expose it in the error (for compatibility with node-mailchimp)
|
||||
const errorBody = err.body ? JSON.parse(err.body) : {}
|
||||
const errWithBody = Object.assign(err, errorBody)
|
||||
throw errWithBody
|
||||
}
|
||||
}
|
||||
|
||||
async get(path) {
|
||||
return await this.request(path, this.fetchOptions)
|
||||
}
|
||||
|
||||
async put(path, body) {
|
||||
const options = Object.assign({}, this.fetchOptions)
|
||||
options.method = 'PUT'
|
||||
options.json = body
|
||||
|
||||
return await this.request(path, options)
|
||||
}
|
||||
|
||||
async delete(path) {
|
||||
const options = Object.assign({}, this.fetchOptions)
|
||||
options.method = 'DELETE'
|
||||
|
||||
return await this.request(path, options)
|
||||
}
|
||||
|
||||
async patch(path, body) {
|
||||
const options = Object.assign({}, this.fetchOptions)
|
||||
options.method = 'PATCH'
|
||||
options.json = body
|
||||
|
||||
return await this.request(path, options)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MailChimpClient
|
|
@ -1,9 +1,9 @@
|
|||
const logger = require('@overleaf/logger')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const crypto = require('crypto')
|
||||
const Mailchimp = require('mailchimp-api-v3')
|
||||
const OError = require('@overleaf/o-error')
|
||||
const { callbackify } = require('util')
|
||||
const MailChimpClient = require('./MailChimpClient')
|
||||
|
||||
function mailchimpIsConfigured() {
|
||||
return Settings.mailchimp != null && Settings.mailchimp.api_key != null
|
||||
|
@ -38,7 +38,7 @@ class NonFatalEmailUpdateError extends OError {
|
|||
}
|
||||
|
||||
function makeMailchimpProvider(listName, listId) {
|
||||
const mailchimp = new Mailchimp(Settings.mailchimp.api_key)
|
||||
const mailchimp = new MailChimpClient(Settings.mailchimp.api_key)
|
||||
const MAILCHIMP_LIST_ID = listId
|
||||
|
||||
return {
|
||||
|
@ -54,7 +54,7 @@ function makeMailchimpProvider(listName, listId) {
|
|||
const result = await mailchimp.get(path)
|
||||
return result?.status === 'subscribed'
|
||||
} catch (err) {
|
||||
if (err.status === 404) {
|
||||
if (err?.response?.status === 404) {
|
||||
return false
|
||||
}
|
||||
throw OError.tag(err, 'error getting newsletter subscriptions status', {
|
||||
|
@ -101,7 +101,7 @@ function makeMailchimpProvider(listName, listId) {
|
|||
'finished unsubscribing user from newsletter'
|
||||
)
|
||||
} catch (err) {
|
||||
if (err.status === 404 || err.status === 405) {
|
||||
if ([404, 405].includes(err?.response?.status)) {
|
||||
// silently ignore users who were never subscribed (404) or previously deleted (405)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -123,7 +123,6 @@
|
|||
"jsonwebtoken": "^9.0.0",
|
||||
"lodash": "^4.17.19",
|
||||
"lru-cache": "^7.10.1",
|
||||
"mailchimp-api-v3": "^1.12.0",
|
||||
"marked": "^4.1.0",
|
||||
"method-override": "^2.3.3",
|
||||
"minimatch": "^7.4.2",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const { RequestFailedError } = require('@overleaf/fetch-utils')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
const MODULE_PATH = '../../../../app/src/Features/Newsletter/NewsletterManager'
|
||||
|
@ -28,11 +29,15 @@ describe('NewsletterManager', function () {
|
|||
|
||||
this.NewsletterManager = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'mailchimp-api-v3': this.Mailchimp,
|
||||
'./MailChimpClient': this.Mailchimp,
|
||||
'@overleaf/settings': this.Settings,
|
||||
},
|
||||
globals: { AbortController },
|
||||
}).promises
|
||||
|
||||
this.NewsletterManager.get = sinon.stub()
|
||||
this.NewsletterManager.delete = sinon.stub()
|
||||
|
||||
this.user = {
|
||||
_id: 'user_id',
|
||||
email: 'overleaf.duck@example.com',
|
||||
|
@ -59,9 +64,14 @@ describe('NewsletterManager', function () {
|
|||
})
|
||||
|
||||
it('returns false on 404', async function () {
|
||||
const err = new Error()
|
||||
err.status = 404
|
||||
this.mailchimp.get.rejects(err)
|
||||
this.mailchimp.get.rejects(
|
||||
new RequestFailedError(
|
||||
'http://some-url',
|
||||
{},
|
||||
{ status: 404 },
|
||||
'Not found'
|
||||
)
|
||||
)
|
||||
const subscribed = await this.NewsletterManager.subscribed(this.user)
|
||||
expect(subscribed).to.be.false
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue