mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #17430 from overleaf/dp-callbackify-class
Add callbackifyClass utility GitOrigin-RevId: 762b800ce0eff2f146147908838162f7d32bd855
This commit is contained in:
parent
c159704ca7
commit
9ef084d73f
5 changed files with 153 additions and 51 deletions
|
@ -8,6 +8,7 @@ module.exports = {
|
||||||
promisifyMultiResult,
|
promisifyMultiResult,
|
||||||
callbackify,
|
callbackify,
|
||||||
callbackifyAll,
|
callbackifyAll,
|
||||||
|
callbackifyClass,
|
||||||
callbackifyMultiResult,
|
callbackifyMultiResult,
|
||||||
expressify,
|
expressify,
|
||||||
expressifyErrorHandler,
|
expressifyErrorHandler,
|
||||||
|
@ -177,6 +178,34 @@ function callbackifyAll(module, opts = {}) {
|
||||||
return callbacks
|
return callbacks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callbackify all methods in a class.
|
||||||
|
*
|
||||||
|
* Options are the same as for callbackifyAll
|
||||||
|
*/
|
||||||
|
function callbackifyClass(cls, opts = {}) {
|
||||||
|
const callbackified = class extends cls {}
|
||||||
|
const { without = [], multiResult = {} } = opts
|
||||||
|
for (const propName of Object.getOwnPropertyNames(cls.prototype)) {
|
||||||
|
if (propName === 'constructor' || without.includes(propName)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const propValue = cls.prototype[propName]
|
||||||
|
if (typeof propValue !== 'function') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (multiResult[propName] != null) {
|
||||||
|
callbackified.prototype[propName] = callbackifyMultiResult(
|
||||||
|
propValue,
|
||||||
|
multiResult[propName]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
callbackified.prototype[propName] = callbackify(propValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return callbackified
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverse the effect of `promisifyMultiResult`.
|
* Reverse the effect of `promisifyMultiResult`.
|
||||||
*
|
*
|
||||||
|
@ -186,7 +215,7 @@ function callbackifyAll(module, opts = {}) {
|
||||||
function callbackifyMultiResult(fn, resultNames) {
|
function callbackifyMultiResult(fn, resultNames) {
|
||||||
function callbackified(...args) {
|
function callbackified(...args) {
|
||||||
const [callback] = args.splice(-1)
|
const [callback] = args.splice(-1)
|
||||||
fn(...args)
|
fn.apply(this, args)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
const cbResults = resultNames.map(resultName => result[resultName])
|
const cbResults = resultNames.map(resultName => result[resultName])
|
||||||
callback(null, ...cbResults)
|
callback(null, ...cbResults)
|
||||||
|
|
|
@ -3,6 +3,7 @@ const {
|
||||||
promisifyAll,
|
promisifyAll,
|
||||||
promisifyClass,
|
promisifyClass,
|
||||||
callbackifyMultiResult,
|
callbackifyMultiResult,
|
||||||
|
callbackifyClass,
|
||||||
callbackifyAll,
|
callbackifyAll,
|
||||||
expressify,
|
expressify,
|
||||||
expressifyErrorHandler,
|
expressifyErrorHandler,
|
||||||
|
@ -327,6 +328,108 @@ describe('callbackifyAll', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('callbackifyClass', function () {
|
||||||
|
describe('basic functionality', function () {
|
||||||
|
before(function () {
|
||||||
|
this.Class = class {
|
||||||
|
constructor(a) {
|
||||||
|
this.a = a
|
||||||
|
}
|
||||||
|
|
||||||
|
async asyncAdd(b) {
|
||||||
|
return this.a + b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Callbackified = callbackifyClass(this.Class)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('callbackifies the class methods', function (done) {
|
||||||
|
const adder = new this.Callbackified(1)
|
||||||
|
adder.asyncAdd(2, (err, sum) => {
|
||||||
|
expect(err).not.to.exist
|
||||||
|
expect(sum).to.equal(3)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('without option', function () {
|
||||||
|
before(function () {
|
||||||
|
this.Class = class {
|
||||||
|
constructor(a) {
|
||||||
|
this.a = a
|
||||||
|
}
|
||||||
|
|
||||||
|
async asyncAdd(b) {
|
||||||
|
return this.a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
syncAdd(b) {
|
||||||
|
return this.a + b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Callbackified = callbackifyClass(this.Class, {
|
||||||
|
without: ['syncAdd'],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not callbackify excluded functions', function () {
|
||||||
|
const adder = new this.Callbackified(10)
|
||||||
|
const sum = adder.syncAdd(12)
|
||||||
|
expect(sum).to.equal(22)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('callbackifies other functions', function (done) {
|
||||||
|
const adder = new this.Callbackified(1)
|
||||||
|
adder.asyncAdd(2, (err, sum) => {
|
||||||
|
expect(err).not.to.exist
|
||||||
|
expect(sum).to.equal(3)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('multiResult option', function () {
|
||||||
|
before(function () {
|
||||||
|
this.Class = class {
|
||||||
|
constructor(a) {
|
||||||
|
this.a = a
|
||||||
|
}
|
||||||
|
|
||||||
|
async asyncAdd(b) {
|
||||||
|
return this.a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
async asyncArithmetic(b) {
|
||||||
|
return { sum: this.a + b, product: this.a * b }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Callbackified = callbackifyClass(this.Class, {
|
||||||
|
multiResult: { asyncArithmetic: ['sum', 'product'] },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('callbackifies multi-result functions', function (done) {
|
||||||
|
const adder = new this.Callbackified(3)
|
||||||
|
adder.asyncArithmetic(6, (err, sum, product) => {
|
||||||
|
expect(err).not.to.exist
|
||||||
|
expect(sum).to.equal(9)
|
||||||
|
expect(product).to.equal(18)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('callbackifies other functions normally', function (done) {
|
||||||
|
const adder = new this.Callbackified(6)
|
||||||
|
adder.asyncAdd(2, (err, sum) => {
|
||||||
|
expect(err).not.to.exist
|
||||||
|
expect(sum).to.equal(8)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('expressify', function () {
|
describe('expressify', function () {
|
||||||
it('should propagate any rejection to the "next" callback', function (done) {
|
it('should propagate any rejection to the "next" callback', function (done) {
|
||||||
const fn = () => Promise.reject(new Error('rejected'))
|
const fn = () => Promise.reject(new Error('rejected'))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const { ObjectId } = require('mongodb')
|
const { ObjectId } = require('mongodb')
|
||||||
const PublisherModel = require('../../../../app/src/models/Publisher').Publisher
|
const PublisherModel = require('../../../../app/src/models/Publisher').Publisher
|
||||||
const { callbackify } = require('util')
|
const { callbackifyClass } = require('@overleaf/promise-utils')
|
||||||
|
|
||||||
let count = parseInt(Math.random() * 999999)
|
let count = parseInt(Math.random() * 999999)
|
||||||
|
|
||||||
|
@ -32,21 +32,7 @@ class PromisifiedPublisher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Publisher extends PromisifiedPublisher {}
|
const Publisher = callbackifyClass(PromisifiedPublisher)
|
||||||
Publisher.promises = class extends PromisifiedPublisher {}
|
Publisher.promises = class extends PromisifiedPublisher {}
|
||||||
|
|
||||||
// callbackify publisher class methods
|
|
||||||
const nonPromiseMethods = ['constructor']
|
|
||||||
Object.getOwnPropertyNames(PromisifiedPublisher.prototype).forEach(
|
|
||||||
methodName => {
|
|
||||||
const method = PromisifiedPublisher.prototype[methodName]
|
|
||||||
if (
|
|
||||||
typeof method === 'function' &&
|
|
||||||
!nonPromiseMethods.includes(methodName)
|
|
||||||
) {
|
|
||||||
Publisher.prototype[methodName] = callbackify(method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = Publisher
|
module.exports = Publisher
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const { db, ObjectId } = require('../../../../app/src/infrastructure/mongodb')
|
const { db, ObjectId } = require('../../../../app/src/infrastructure/mongodb')
|
||||||
const { expect } = require('chai')
|
const { expect } = require('chai')
|
||||||
const { promisify } = require('util')
|
const { promisifyClass } = require('@overleaf/promise-utils')
|
||||||
const SubscriptionUpdater = require('../../../../app/src/Features/Subscription/SubscriptionUpdater')
|
const SubscriptionUpdater = require('../../../../app/src/Features/Subscription/SubscriptionUpdater')
|
||||||
const PermissionsManager = require('../../../../app/src/Features/Authorization/PermissionsManager')
|
const PermissionsManager = require('../../../../app/src/Features/Authorization/PermissionsManager')
|
||||||
const SSOConfigManager = require('../../../../modules/group-settings/app/src/sso/SSOConfigManager')
|
const SSOConfigManager = require('../../../../modules/group-settings/app/src/sso/SSOConfigManager')
|
||||||
|
@ -192,16 +192,8 @@ class Subscription {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Subscription.promises = class extends Subscription {}
|
Subscription.promises = promisifyClass(Subscription, {
|
||||||
|
without: ['getCapabilities'],
|
||||||
// promisify User class methods - works for methods with 0-1 output parameters,
|
|
||||||
// otherwise we will need to implement the method manually instead
|
|
||||||
const nonPromiseMethods = ['constructor', 'getCapabilities']
|
|
||||||
Object.getOwnPropertyNames(Subscription.prototype).forEach(methodName => {
|
|
||||||
const method = Subscription.prototype[methodName]
|
|
||||||
if (typeof method === 'function' && !nonPromiseMethods.includes(methodName)) {
|
|
||||||
Subscription.promises.prototype[methodName] = promisify(method)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Subscription.promises.prototype.inviteUser = async function (adminUser, email) {
|
Subscription.promises.prototype.inviteUser = async function (adminUser, email) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ const { db, ObjectId } = require('../../../../app/src/infrastructure/mongodb')
|
||||||
const UserModel = require('../../../../app/src/models/User').User
|
const UserModel = require('../../../../app/src/models/User').User
|
||||||
const UserUpdater = require('../../../../app/src/Features/User/UserUpdater')
|
const UserUpdater = require('../../../../app/src/Features/User/UserUpdater')
|
||||||
const AuthenticationManager = require('../../../../app/src/Features/Authentication/AuthenticationManager')
|
const AuthenticationManager = require('../../../../app/src/Features/Authentication/AuthenticationManager')
|
||||||
const { promisify } = require('util')
|
const { promisifyClass } = require('@overleaf/promise-utils')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
|
|
||||||
|
@ -1026,28 +1026,20 @@ class User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
User.promises = class extends User {
|
User.promises = promisifyClass(User, {
|
||||||
doRequest(method, params) {
|
without: ['setExtraAttributes'],
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.request[method.toLowerCase()](params, (err, response, body) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err)
|
|
||||||
} else {
|
|
||||||
resolve({ response, body })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// promisify User class methods - works for methods with 0-1 output parameters,
|
|
||||||
// otherwise we will need to implement the method manually instead
|
|
||||||
const nonPromiseMethods = ['constructor', 'setExtraAttributes']
|
|
||||||
Object.getOwnPropertyNames(User.prototype).forEach(methodName => {
|
|
||||||
const method = User.prototype[methodName]
|
|
||||||
if (typeof method === 'function' && !nonPromiseMethods.includes(methodName)) {
|
|
||||||
User.promises.prototype[methodName] = promisify(method)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
User.promises.prototype.doRequest = async function (method, params) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.request[method.toLowerCase()](params, (err, response, body) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve({ response, body })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = User
|
module.exports = User
|
||||||
|
|
Loading…
Reference in a new issue