diff --git a/services/web/app/src/Features/Institutions/InstitutionsAPI.js b/services/web/app/src/Features/Institutions/InstitutionsAPI.js index bdb575c33c..e6d5411a8a 100644 --- a/services/web/app/src/Features/Institutions/InstitutionsAPI.js +++ b/services/web/app/src/Features/Institutions/InstitutionsAPI.js @@ -10,6 +10,8 @@ const { InvalidInstitutionalEmailError, } = require('../Errors/Errors') const { fetchJson, fetchNothing } = require('@overleaf/fetch-utils') +const { promiseMapWithLimit } = require('@overleaf/promise-utils') +const Modules = require('../../infrastructure/Modules') function _makeRequestOptions(options) { const requestOptions = { @@ -151,7 +153,37 @@ function getUserAffiliations(userId, callback) { path: `/api/v2/users/${userId.toString()}/affiliations`, defaultErrorMessage: "Couldn't get user affiliations", }, - (error, body) => callback(error, body || []) + async (error, body) => { + if (error) { + return callback(error, []) + } + + const affiliations = [] + + if (body?.length > 0) { + const concurrencyLimit = 10 + await promiseMapWithLimit(concurrencyLimit, body, async affiliation => { + if (!affiliation.institution.commonsAccount) { + const group = ( + await Modules.promises.hooks.fire( + 'getGroupWithDomainCaptureByV1Id', + affiliation.institution.id + ) + )?.[0] + + if (group) { + affiliation.group = { + domainCaptureEnabled: Boolean(group.domainCaptureEnabled), + } + } + } + + affiliations.push(affiliation) + }) + } + + callback(null, affiliations) + } ) } diff --git a/services/web/test/unit/src/Institutions/InstitutionsAPITests.js b/services/web/test/unit/src/Institutions/InstitutionsAPITests.js index f3458f51f7..fcd36dd1ad 100644 --- a/services/web/test/unit/src/Institutions/InstitutionsAPITests.js +++ b/services/web/test/unit/src/Institutions/InstitutionsAPITests.js @@ -33,6 +33,13 @@ describe('InstitutionsAPI', function () { .returns(this.ipMatcherNotification), }, }, + '../../infrastructure/Modules': (this.Modules = { + promises: { + hooks: { + fire: sinon.stub(), + }, + }, + }), }, }) @@ -118,8 +125,15 @@ describe('InstitutionsAPI', function () { }) describe('getUserAffiliations', function () { - it('get affiliations', async function () { - const responseBody = [{ foo: 'bar' }] + it('get affiliations with commons', async function () { + const responseBody = [ + { + foo: 'bar', + institution: { + commonsAccount: true, + }, + }, + ] this.request.callsArgWith(1, null, { statusCode: 201 }, responseBody) const body = await this.InstitutionsAPI.promises.getUserAffiliations( this.stubbedUser._id @@ -130,8 +144,42 @@ describe('InstitutionsAPI', function () { requestOptions.url.should.equal(expectedUrl) requestOptions.method.should.equal('GET') requestOptions.maxAttempts.should.equal(3) + this.Modules.promises.hooks.fire.should.not.have.been.called expect(requestOptions.body).not.to.exist - body.should.equal(responseBody) + expect(body).to.deep.equal(responseBody) + }) + + it('get affiliations with domain capture for groups', async function () { + const responseBody = [ + { + id: '123abc', + foo: 'bar', + institution: { + commonsAccount: false, + }, + }, + ] + this.request.callsArgWith(1, null, { statusCode: 201 }, responseBody) + this.Modules.promises.hooks.fire.resolves([ + { domainCaptureEnabled: true }, + ]) + const body = await this.InstitutionsAPI.promises.getUserAffiliations( + this.stubbedUser._id + ) + this.request.calledOnce.should.equal(true) + const requestOptions = this.request.lastCall.args[0] + const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations` + requestOptions.url.should.equal(expectedUrl) + requestOptions.method.should.equal('GET') + requestOptions.maxAttempts.should.equal(3) + this.Modules.promises.hooks.fire.should.have.been.calledWith( + 'getGroupWithDomainCaptureByV1Id', + responseBody[0].institution.id + ) + expect(requestOptions.body).not.to.exist + expect(body).to.deep.equal([ + { ...responseBody[0], group: { domainCaptureEnabled: true } }, + ]) }) it('handle error', async function () {