mirror of
https://github.com/overleaf/overleaf.git
synced 2025-03-15 05:11:57 +00:00
Merge pull request #3732 from overleaf/tm-recurly-create-admin-link
Create link to Recurly in admin panel GitOrigin-RevId: 214802e9fbe16954d455ac04eb176ff27890769c
This commit is contained in:
parent
1c54a15e42
commit
fee245b570
5 changed files with 181 additions and 0 deletions
53
services/web/app/src/Features/Subscription/RecurlyClient.js
Normal file
53
services/web/app/src/Features/Subscription/RecurlyClient.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
const recurly = require('recurly')
|
||||
const Settings = require('settings-sharelatex')
|
||||
const logger = require('logger-sharelatex')
|
||||
const { callbackify } = require('util')
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
|
||||
const recurlySettings = Settings.apis.recurly
|
||||
const recurlyApiKey = recurlySettings ? recurlySettings.apiKey : undefined
|
||||
|
||||
const client = new recurly.Client(recurlyApiKey)
|
||||
|
||||
module.exports = {
|
||||
errors: recurly.errors,
|
||||
|
||||
getAccountForUserId: callbackify(getAccountForUserId),
|
||||
createAccountForUserId: callbackify(createAccountForUserId),
|
||||
|
||||
promises: {
|
||||
getAccountForUserId,
|
||||
createAccountForUserId
|
||||
}
|
||||
}
|
||||
|
||||
async function getAccountForUserId(userId) {
|
||||
try {
|
||||
return await client.getAccount(`code-${userId}`)
|
||||
} catch (err) {
|
||||
if (err instanceof recurly.errors.NotFoundError) {
|
||||
// An expected error, we don't need to handle it, just return nothing
|
||||
logger.debug({ userId }, 'no recurly account found for user')
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createAccountForUserId(userId) {
|
||||
const user = await UserGetter.promises.getUser(userId, {
|
||||
_id: 1,
|
||||
first_name: 1,
|
||||
last_name: 1,
|
||||
email: 1
|
||||
})
|
||||
const accountCreate = {
|
||||
code: user._id.toString(),
|
||||
email: user.email,
|
||||
firstName: user.first_name,
|
||||
lastName: user.last_name
|
||||
}
|
||||
const account = await client.createAccount(accountCreate)
|
||||
logger.log({ userId, account }, 'created recurly account')
|
||||
return account
|
||||
}
|
5
services/web/package-lock.json
generated
5
services/web/package-lock.json
generated
|
@ -25916,6 +25916,11 @@
|
|||
"resolve": "^1.1.6"
|
||||
}
|
||||
},
|
||||
"recurly": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/recurly/-/recurly-4.0.0.tgz",
|
||||
"integrity": "sha512-IXWMAUGi9p7dH2w2KK7ozfAIqD8FVLs5UQTzW7BPTQk3TnqcSvZVCk+hT9Esa2QLV84q//xoWZACxsHvKGR7Yg=="
|
||||
},
|
||||
"recursive-readdir": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
|
||||
|
|
|
@ -142,6 +142,7 @@
|
|||
"react-linkify": "^1.0.0-alpha",
|
||||
"react2angular": "^4.0.6",
|
||||
"react2angular-shared-context": "^1.1.0",
|
||||
"recurly": "^4.0.0",
|
||||
"request": "^2.88.2",
|
||||
"request-promise-native": "^1.0.8",
|
||||
"requestretry": "^1.13.0",
|
||||
|
|
|
@ -27,6 +27,7 @@ module.exports =
|
|||
# Set up our own mock recurly server
|
||||
url: 'http://localhost:6034'
|
||||
subdomain: 'test'
|
||||
apiKey: 'private-nonsense'
|
||||
|
||||
# for registration via SL, set enableLegacyRegistration to true
|
||||
# for registration via Overleaf v1, set enableLegacyLogin to true
|
||||
|
|
121
services/web/test/unit/src/Subscription/RecurlyClientTests.js
Normal file
121
services/web/test/unit/src/Subscription/RecurlyClientTests.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
const sinon = require('sinon')
|
||||
const sinonChai = require('sinon-chai')
|
||||
const chai = require('chai')
|
||||
const chaiAsPromised = require('chai-as-promised')
|
||||
chai.use(sinonChai)
|
||||
chai.use(chaiAsPromised)
|
||||
const { expect } = chai
|
||||
const recurly = require('recurly')
|
||||
const modulePath = '../../../../app/src/Features/Subscription/RecurlyClient'
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
|
||||
describe('RecurlyClient', function() {
|
||||
beforeEach(function() {
|
||||
this.settings = {
|
||||
apis: {
|
||||
recurly: {
|
||||
apiKey: 'nonsense',
|
||||
privateKey: 'private_nonsense'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.user = { _id: '123456', email: 'joe@example.com', first_name: 'Joe' }
|
||||
this.recurlyAccount = new recurly.Account()
|
||||
Object.assign(this.recurlyAccount, { code: this.user._id })
|
||||
|
||||
this.UserGetter = {
|
||||
promises: {
|
||||
getUser: sinon.stub().callsFake(userId => {
|
||||
if (userId === this.user._id) {
|
||||
return this.user
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let client
|
||||
this.client = client = {
|
||||
getAccount: sinon.stub()
|
||||
}
|
||||
this.recurly = {
|
||||
errors: recurly.errors,
|
||||
Client: function() {
|
||||
return client
|
||||
}
|
||||
}
|
||||
|
||||
return (this.RecurlyClient = SandboxedModule.require(modulePath, {
|
||||
globals: {
|
||||
console: console
|
||||
},
|
||||
requires: {
|
||||
'settings-sharelatex': this.settings,
|
||||
recurly: this.recurly,
|
||||
'logger-sharelatex': {
|
||||
err: sinon.stub(),
|
||||
error: sinon.stub(),
|
||||
warn: sinon.stub(),
|
||||
log: sinon.stub(),
|
||||
debug: sinon.stub()
|
||||
},
|
||||
'../User/UserGetter': this.UserGetter
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
describe('initalizing recurly client with undefined API key parameter', function() {
|
||||
it('should create a client without error', function() {
|
||||
let testClient
|
||||
expect(() => {
|
||||
testClient = new recurly.Client(undefined)
|
||||
}).to.not.throw()
|
||||
expect(testClient).to.be.instanceOf(recurly.Client)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAccountForUserId', function() {
|
||||
it('should return an Account if one exists', async function() {
|
||||
this.client.getAccount = sinon.stub().resolves(this.recurlyAccount)
|
||||
await expect(
|
||||
this.RecurlyClient.promises.getAccountForUserId(this.user._id)
|
||||
)
|
||||
.to.eventually.be.an.instanceOf(recurly.Account)
|
||||
.that.has.property('code', this.user._id)
|
||||
})
|
||||
|
||||
it('should return nothing if no account found', async function() {
|
||||
this.client.getAccount = sinon
|
||||
.stub()
|
||||
.throws(new recurly.errors.NotFoundError())
|
||||
expect(
|
||||
this.RecurlyClient.promises.getAccountForUserId('nonsense')
|
||||
).to.eventually.equal(undefined)
|
||||
})
|
||||
|
||||
it('should re-throw caught errors', async function() {
|
||||
this.client.getAccount = sinon.stub().throws()
|
||||
await expect(
|
||||
this.RecurlyClient.promises.getAccountForUserId(this.user._id)
|
||||
).to.eventually.be.rejectedWith(Error)
|
||||
})
|
||||
})
|
||||
|
||||
describe('createAccountForUserId', function() {
|
||||
it('should return the Account as created by recurly', async function() {
|
||||
this.client.createAccount = sinon.stub().resolves(this.recurlyAccount)
|
||||
await expect(
|
||||
this.RecurlyClient.promises.createAccountForUserId(this.user._id)
|
||||
)
|
||||
.to.eventually.be.an.instanceOf(recurly.Account)
|
||||
.that.has.property('code', this.user._id)
|
||||
})
|
||||
|
||||
it('should throw any API errors', async function() {
|
||||
this.client.createAccount = sinon.stub().throws()
|
||||
await expect(
|
||||
this.RecurlyClient.promises.createAccountForUserId(this.user._id)
|
||||
).to.eventually.be.rejectedWith(Error)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue