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==",
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/make-dir": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
|
@ -43585,7 +43513,6 @@
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"lru-cache": "^7.10.1",
|
"lru-cache": "^7.10.1",
|
||||||
"mailchimp-api-v3": "^1.12.0",
|
|
||||||
"marked": "^4.1.0",
|
"marked": "^4.1.0",
|
||||||
"method-override": "^2.3.3",
|
"method-override": "^2.3.3",
|
||||||
"minimatch": "^7.4.2",
|
"minimatch": "^7.4.2",
|
||||||
|
@ -52263,7 +52190,6 @@
|
||||||
"less-loader": "^11.1.3",
|
"less-loader": "^11.1.3",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"lru-cache": "^7.10.1",
|
"lru-cache": "^7.10.1",
|
||||||
"mailchimp-api-v3": "^1.12.0",
|
|
||||||
"marked": "^4.1.0",
|
"marked": "^4.1.0",
|
||||||
"match-sorter": "^6.2.0",
|
"match-sorter": "^6.2.0",
|
||||||
"mathjax": "^3.2.2",
|
"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": {
|
"make-dir": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
"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 logger = require('@overleaf/logger')
|
||||||
const Settings = require('@overleaf/settings')
|
const Settings = require('@overleaf/settings')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const Mailchimp = require('mailchimp-api-v3')
|
|
||||||
const OError = require('@overleaf/o-error')
|
const OError = require('@overleaf/o-error')
|
||||||
const { callbackify } = require('util')
|
const { callbackify } = require('util')
|
||||||
|
const MailChimpClient = require('./MailChimpClient')
|
||||||
|
|
||||||
function mailchimpIsConfigured() {
|
function mailchimpIsConfigured() {
|
||||||
return Settings.mailchimp != null && Settings.mailchimp.api_key != null
|
return Settings.mailchimp != null && Settings.mailchimp.api_key != null
|
||||||
|
@ -38,7 +38,7 @@ class NonFatalEmailUpdateError extends OError {
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeMailchimpProvider(listName, listId) {
|
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
|
const MAILCHIMP_LIST_ID = listId
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -54,7 +54,7 @@ function makeMailchimpProvider(listName, listId) {
|
||||||
const result = await mailchimp.get(path)
|
const result = await mailchimp.get(path)
|
||||||
return result?.status === 'subscribed'
|
return result?.status === 'subscribed'
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.status === 404) {
|
if (err?.response?.status === 404) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
throw OError.tag(err, 'error getting newsletter subscriptions status', {
|
throw OError.tag(err, 'error getting newsletter subscriptions status', {
|
||||||
|
@ -101,7 +101,7 @@ function makeMailchimpProvider(listName, listId) {
|
||||||
'finished unsubscribing user from newsletter'
|
'finished unsubscribing user from newsletter'
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} 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)
|
// silently ignore users who were never subscribed (404) or previously deleted (405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,6 @@
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"lru-cache": "^7.10.1",
|
"lru-cache": "^7.10.1",
|
||||||
"mailchimp-api-v3": "^1.12.0",
|
|
||||||
"marked": "^4.1.0",
|
"marked": "^4.1.0",
|
||||||
"method-override": "^2.3.3",
|
"method-override": "^2.3.3",
|
||||||
"minimatch": "^7.4.2",
|
"minimatch": "^7.4.2",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const { expect } = require('chai')
|
const { expect } = require('chai')
|
||||||
const sinon = require('sinon')
|
const sinon = require('sinon')
|
||||||
|
const { RequestFailedError } = require('@overleaf/fetch-utils')
|
||||||
const SandboxedModule = require('sandboxed-module')
|
const SandboxedModule = require('sandboxed-module')
|
||||||
|
|
||||||
const MODULE_PATH = '../../../../app/src/Features/Newsletter/NewsletterManager'
|
const MODULE_PATH = '../../../../app/src/Features/Newsletter/NewsletterManager'
|
||||||
|
@ -28,11 +29,15 @@ describe('NewsletterManager', function () {
|
||||||
|
|
||||||
this.NewsletterManager = SandboxedModule.require(MODULE_PATH, {
|
this.NewsletterManager = SandboxedModule.require(MODULE_PATH, {
|
||||||
requires: {
|
requires: {
|
||||||
'mailchimp-api-v3': this.Mailchimp,
|
'./MailChimpClient': this.Mailchimp,
|
||||||
'@overleaf/settings': this.Settings,
|
'@overleaf/settings': this.Settings,
|
||||||
},
|
},
|
||||||
|
globals: { AbortController },
|
||||||
}).promises
|
}).promises
|
||||||
|
|
||||||
|
this.NewsletterManager.get = sinon.stub()
|
||||||
|
this.NewsletterManager.delete = sinon.stub()
|
||||||
|
|
||||||
this.user = {
|
this.user = {
|
||||||
_id: 'user_id',
|
_id: 'user_id',
|
||||||
email: 'overleaf.duck@example.com',
|
email: 'overleaf.duck@example.com',
|
||||||
|
@ -59,9 +64,14 @@ describe('NewsletterManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns false on 404', async function () {
|
it('returns false on 404', async function () {
|
||||||
const err = new Error()
|
this.mailchimp.get.rejects(
|
||||||
err.status = 404
|
new RequestFailedError(
|
||||||
this.mailchimp.get.rejects(err)
|
'http://some-url',
|
||||||
|
{},
|
||||||
|
{ status: 404 },
|
||||||
|
'Not found'
|
||||||
|
)
|
||||||
|
)
|
||||||
const subscribed = await this.NewsletterManager.subscribed(this.user)
|
const subscribed = await this.NewsletterManager.subscribed(this.user)
|
||||||
expect(subscribed).to.be.false
|
expect(subscribed).to.be.false
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue