mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-24 05:52:48 +00:00
Merge pull request #16444 from overleaf/jel-v1-api
[web] Begin promisifying InstitutionsAPI GitOrigin-RevId: aa615e22d3fc8aa77255a26dcdab0e2ce28d93c0
This commit is contained in:
parent
43b2fe4a3a
commit
45d44d2fe6
2 changed files with 391 additions and 227 deletions
|
@ -1,3 +1,4 @@
|
|||
const { callbackify } = require('util')
|
||||
const OError = require('@overleaf/o-error')
|
||||
const logger = require('@overleaf/logger')
|
||||
const metrics = require('@overleaf/metrics')
|
||||
|
@ -9,210 +10,306 @@ const {
|
|||
V1ConnectionError,
|
||||
InvalidInstitutionalEmailError,
|
||||
} = require('../Errors/Errors')
|
||||
const { fetchJson, fetchNothing } = require('@overleaf/fetch-utils')
|
||||
|
||||
function _makeRequestOptions(options) {
|
||||
const requestOptions = {
|
||||
method: options.method,
|
||||
basicAuth: { user: settings.apis.v1.user, password: settings.apis.v1.pass },
|
||||
signal: AbortSignal.timeout(settings.apis.v1.timeout),
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
requestOptions.json = options.body
|
||||
}
|
||||
|
||||
return requestOptions
|
||||
}
|
||||
|
||||
function _responseErrorHandling(options, error) {
|
||||
const status = error.response.status
|
||||
|
||||
if (status >= 500) {
|
||||
throw new V1ConnectionError({
|
||||
message: 'error getting affiliations from v1',
|
||||
info: {
|
||||
status,
|
||||
body: error.body,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
let errorBody
|
||||
|
||||
try {
|
||||
if (error.body) {
|
||||
errorBody = JSON.parse(error.body)
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
let errorMessage
|
||||
if (errorBody?.errors) {
|
||||
errorMessage = `${status}: ${errorBody.errors}`
|
||||
} else {
|
||||
errorMessage = `${options.defaultErrorMessage}: ${status}`
|
||||
}
|
||||
|
||||
throw new OError(errorMessage, { status })
|
||||
}
|
||||
|
||||
async function _affiliationRequestFetchJson(options) {
|
||||
if (!settings.apis.v1.url) {
|
||||
return
|
||||
} // service is not configured
|
||||
|
||||
const url = `${settings.apis.v1.url}${options.path}`
|
||||
|
||||
const requestOptions = _makeRequestOptions(options)
|
||||
|
||||
try {
|
||||
return await fetchJson(url, requestOptions)
|
||||
} catch (error) {
|
||||
_responseErrorHandling(options, error)
|
||||
}
|
||||
}
|
||||
|
||||
async function _affiliationRequestFetchNothing(options) {
|
||||
if (!settings.apis.v1.url) {
|
||||
return
|
||||
} // service is not configured
|
||||
|
||||
const url = `${settings.apis.v1.url}${options.path}`
|
||||
|
||||
const requestOptions = _makeRequestOptions(options)
|
||||
|
||||
try {
|
||||
await fetchNothing(url, requestOptions)
|
||||
} catch (error) {
|
||||
_responseErrorHandling(options, error)
|
||||
}
|
||||
}
|
||||
|
||||
async function _affiliationRequestFetchNothing404Ok(options) {
|
||||
try {
|
||||
await _affiliationRequestFetchNothing(options)
|
||||
} catch (error) {
|
||||
const status = error.info?.status
|
||||
if (status !== 404) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getInstitutionAffiliations(institutionId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/${institutionId.toString()}/affiliations`,
|
||||
defaultErrorMessage: "Couldn't get institution affiliations",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
}
|
||||
|
||||
function getConfirmedInstitutionAffiliations(institutionId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/${institutionId.toString()}/confirmed_affiliations`,
|
||||
defaultErrorMessage: "Couldn't get institution affiliations",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
}
|
||||
|
||||
function getInstitutionAffiliationsCounts(institutionId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/${institutionId.toString()}/affiliations_counts`,
|
||||
defaultErrorMessage: "Couldn't get institution counts",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
}
|
||||
|
||||
function getLicencesForAnalytics(lag, queryDate, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/institutions_licences`,
|
||||
body: { query_date: queryDate, lag },
|
||||
defaultErrorMessage: 'Could not get institutions licences',
|
||||
},
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
function getUserAffiliations(userId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations`,
|
||||
defaultErrorMessage: "Couldn't get user affiliations",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
}
|
||||
|
||||
async function getUsersNeedingReconfirmationsLapsedProcessed() {
|
||||
return await _affiliationRequestFetchJson({
|
||||
method: 'GET',
|
||||
path: '/api/v2/institutions/need_reconfirmation_lapsed_processed',
|
||||
defaultErrorMessage:
|
||||
'Could not get users that need reconfirmations lapsed processed',
|
||||
})
|
||||
}
|
||||
|
||||
async function addAffiliation(userId, email, affiliationOptions) {
|
||||
const {
|
||||
university,
|
||||
department,
|
||||
role,
|
||||
confirmedAt,
|
||||
entitlement,
|
||||
rejectIfBlocklisted,
|
||||
} = affiliationOptions
|
||||
|
||||
try {
|
||||
await _affiliationRequestFetchNothing({
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations`,
|
||||
body: {
|
||||
email,
|
||||
university,
|
||||
department,
|
||||
role,
|
||||
confirmedAt,
|
||||
entitlement,
|
||||
rejectIfBlocklisted,
|
||||
},
|
||||
defaultErrorMessage: "Couldn't create affiliation",
|
||||
})
|
||||
} catch (error) {
|
||||
if (error.info?.status === 422) {
|
||||
throw new InvalidInstitutionalEmailError(error.message).withCause(error)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
if (!university) {
|
||||
return
|
||||
}
|
||||
|
||||
// have notifications delete any ip matcher notifications for this university
|
||||
try {
|
||||
await NotificationsBuilder.promises
|
||||
.ipMatcherAffiliation(userId)
|
||||
.read(university.id)
|
||||
} catch (err) {
|
||||
// log and ignore error
|
||||
logger.err({ err }, 'Something went wrong marking ip notifications read')
|
||||
}
|
||||
}
|
||||
|
||||
async function removeAffiliation(userId, email) {
|
||||
await _affiliationRequestFetchNothing404Ok({
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations/remove`,
|
||||
body: { email },
|
||||
defaultErrorMessage: "Couldn't remove affiliation",
|
||||
})
|
||||
}
|
||||
|
||||
function endorseAffiliation(userId, email, role, department, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations/endorse`,
|
||||
body: { email, role, department },
|
||||
defaultErrorMessage: "Couldn't endorse affiliation",
|
||||
},
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
function deleteAffiliations(userId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations`,
|
||||
defaultErrorMessage: "Couldn't delete affiliations",
|
||||
},
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
function addEntitlement(userId, email, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId}/affiliations/add_entitlement`,
|
||||
body: { email },
|
||||
defaultErrorMessage: "Couldn't add entitlement",
|
||||
},
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
function removeEntitlement(userId, email, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId}/affiliations/remove_entitlement`,
|
||||
body: { email },
|
||||
defaultErrorMessage: "Couldn't remove entitlement",
|
||||
extraSuccessStatusCodes: [404],
|
||||
},
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
function sendUsersWithReconfirmationsLapsedProcessed(users, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v2/institutions/reconfirmation_lapsed_processed',
|
||||
body: { users },
|
||||
defaultErrorMessage:
|
||||
'Could not update reconfirmation_lapsed_processed_at',
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
}
|
||||
|
||||
const InstitutionsAPI = {
|
||||
getInstitutionAffiliations(institutionId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/${institutionId.toString()}/affiliations`,
|
||||
defaultErrorMessage: "Couldn't get institution affiliations",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
},
|
||||
getInstitutionAffiliations,
|
||||
|
||||
getConfirmedInstitutionAffiliations(institutionId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/${institutionId.toString()}/confirmed_affiliations`,
|
||||
defaultErrorMessage: "Couldn't get institution affiliations",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
},
|
||||
getConfirmedInstitutionAffiliations,
|
||||
|
||||
getInstitutionAffiliationsCounts(institutionId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/${institutionId.toString()}/affiliations_counts`,
|
||||
defaultErrorMessage: "Couldn't get institution counts",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
},
|
||||
getInstitutionAffiliationsCounts,
|
||||
|
||||
getLicencesForAnalytics(lag, queryDate, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/institutions/institutions_licences`,
|
||||
body: { query_date: queryDate, lag },
|
||||
defaultErrorMessage: 'Could not get institutions licences',
|
||||
},
|
||||
callback
|
||||
)
|
||||
},
|
||||
getLicencesForAnalytics,
|
||||
|
||||
getUserAffiliations(userId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations`,
|
||||
defaultErrorMessage: "Couldn't get user affiliations",
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
},
|
||||
getUserAffiliations,
|
||||
|
||||
getUsersNeedingReconfirmationsLapsedProcessed(callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v2/institutions/need_reconfirmation_lapsed_processed',
|
||||
defaultErrorMessage:
|
||||
'Could not get users that need reconfirmations lapsed processed',
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
},
|
||||
getUsersNeedingReconfirmationsLapsedProcessed: callbackify(
|
||||
getUsersNeedingReconfirmationsLapsedProcessed
|
||||
),
|
||||
|
||||
addAffiliation(userId, email, affiliationOptions, callback) {
|
||||
if (!callback) {
|
||||
// affiliationOptions is optional
|
||||
callback = affiliationOptions
|
||||
affiliationOptions = {}
|
||||
}
|
||||
addAffiliation: callbackify(addAffiliation),
|
||||
|
||||
const {
|
||||
university,
|
||||
department,
|
||||
role,
|
||||
confirmedAt,
|
||||
entitlement,
|
||||
rejectIfBlocklisted,
|
||||
} = affiliationOptions
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations`,
|
||||
body: {
|
||||
email,
|
||||
university,
|
||||
department,
|
||||
role,
|
||||
confirmedAt,
|
||||
entitlement,
|
||||
rejectIfBlocklisted,
|
||||
},
|
||||
defaultErrorMessage: "Couldn't create affiliation",
|
||||
},
|
||||
function (error, body) {
|
||||
if (error) {
|
||||
if (error.info && error.info.statusCode === 422) {
|
||||
return callback(
|
||||
new InvalidInstitutionalEmailError(error.message).withCause(error)
|
||||
)
|
||||
}
|
||||
return callback(error)
|
||||
}
|
||||
if (!university) {
|
||||
return callback(null, body)
|
||||
}
|
||||
removeAffiliation: callbackify(removeAffiliation),
|
||||
|
||||
// have notifications delete any ip matcher notifications for this university
|
||||
NotificationsBuilder.ipMatcherAffiliation(userId).read(
|
||||
university.id,
|
||||
function (err) {
|
||||
if (err) {
|
||||
// log and ignore error
|
||||
logger.err(
|
||||
{ err },
|
||||
'Something went wrong marking ip notifications read'
|
||||
)
|
||||
}
|
||||
callback(null, body)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
endorseAffiliation,
|
||||
|
||||
removeAffiliation(userId, email, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations/remove`,
|
||||
body: { email },
|
||||
extraSuccessStatusCodes: [404], // `Not Found` responses are considered successful
|
||||
defaultErrorMessage: "Couldn't remove affiliation",
|
||||
},
|
||||
callback
|
||||
)
|
||||
},
|
||||
deleteAffiliations,
|
||||
|
||||
endorseAffiliation(userId, email, role, department, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations/endorse`,
|
||||
body: { email, role, department },
|
||||
defaultErrorMessage: "Couldn't endorse affiliation",
|
||||
},
|
||||
callback
|
||||
)
|
||||
},
|
||||
addEntitlement,
|
||||
|
||||
deleteAffiliations(userId, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: `/api/v2/users/${userId.toString()}/affiliations`,
|
||||
defaultErrorMessage: "Couldn't delete affiliations",
|
||||
},
|
||||
callback
|
||||
)
|
||||
},
|
||||
removeEntitlement,
|
||||
|
||||
addEntitlement(userId, email, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId}/affiliations/add_entitlement`,
|
||||
body: { email },
|
||||
defaultErrorMessage: "Couldn't add entitlement",
|
||||
},
|
||||
callback
|
||||
)
|
||||
},
|
||||
|
||||
removeEntitlement(userId, email, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: `/api/v2/users/${userId}/affiliations/remove_entitlement`,
|
||||
body: { email },
|
||||
defaultErrorMessage: "Couldn't remove entitlement",
|
||||
extraSuccessStatusCodes: [404],
|
||||
},
|
||||
callback
|
||||
)
|
||||
},
|
||||
|
||||
sendUsersWithReconfirmationsLapsedProcessed(users, callback) {
|
||||
makeAffiliationRequest(
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v2/institutions/reconfirmation_lapsed_processed',
|
||||
body: { users },
|
||||
defaultErrorMessage:
|
||||
'Could not update reconfirmation_lapsed_processed_at',
|
||||
},
|
||||
(error, body) => callback(error, body || [])
|
||||
)
|
||||
},
|
||||
sendUsersWithReconfirmationsLapsedProcessed,
|
||||
}
|
||||
|
||||
function makeAffiliationRequest(options, callback) {
|
||||
|
@ -280,8 +377,6 @@ function makeAffiliationRequest(options, callback) {
|
|||
'getInstitutionAffiliations',
|
||||
'getConfirmedInstitutionAffiliations',
|
||||
'getUserAffiliations',
|
||||
'addAffiliation',
|
||||
'removeAffiliation',
|
||||
].map(method =>
|
||||
metrics.timeAsyncMethod(
|
||||
InstitutionsAPI,
|
||||
|
@ -291,5 +386,17 @@ function makeAffiliationRequest(options, callback) {
|
|||
)
|
||||
)
|
||||
|
||||
InstitutionsAPI.promises = promisifyAll(InstitutionsAPI)
|
||||
InstitutionsAPI.promises = promisifyAll(InstitutionsAPI, {
|
||||
without: [
|
||||
'addAffiliation',
|
||||
'removeAffiliation',
|
||||
'getUsersNeedingReconfirmationsLapsedProcessed',
|
||||
],
|
||||
})
|
||||
|
||||
InstitutionsAPI.promises.addAffiliation = addAffiliation
|
||||
InstitutionsAPI.promises.removeAffiliation = removeAffiliation
|
||||
InstitutionsAPI.promises.getUsersNeedingReconfirmationsLapsedProcessed =
|
||||
getUsersNeedingReconfirmationsLapsedProcessed
|
||||
|
||||
module.exports = InstitutionsAPI
|
||||
|
|
|
@ -10,10 +10,13 @@ const Errors = require('../../../../app/src/Features/Errors/Errors')
|
|||
|
||||
describe('InstitutionsAPI', function () {
|
||||
beforeEach(function () {
|
||||
this.settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } }
|
||||
this.settings = {
|
||||
apis: { v1: { url: 'v1.url', user: '', pass: '', timeout: 5000 } },
|
||||
}
|
||||
this.request = sinon.stub()
|
||||
this.fetchNothing = sinon.stub()
|
||||
this.ipMatcherNotification = {
|
||||
read: (this.markAsReadIpMatcher = sinon.stub().callsArgWith(1, null)),
|
||||
read: (this.markAsReadIpMatcher = sinon.stub().resolves()),
|
||||
}
|
||||
this.InstitutionsAPI = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
|
@ -22,10 +25,16 @@ describe('InstitutionsAPI', function () {
|
|||
},
|
||||
'@overleaf/settings': this.settings,
|
||||
requestretry: this.request,
|
||||
'@overleaf/fetch-utils': {
|
||||
fetchNothing: this.fetchNothing,
|
||||
fetchJson: (this.fetchJson = sinon.stub()),
|
||||
},
|
||||
'../Notifications/NotificationsBuilder': {
|
||||
ipMatcherAffiliation: sinon
|
||||
.stub()
|
||||
.returns(this.ipMatcherNotification),
|
||||
promises: {
|
||||
ipMatcherAffiliation: sinon
|
||||
.stub()
|
||||
.returns(this.ipMatcherNotification),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -162,14 +171,14 @@ describe('InstitutionsAPI', function () {
|
|||
|
||||
describe('getUsersNeedingReconfirmationsLapsedProcessed', function () {
|
||||
it('get the list of users', function (done) {
|
||||
this.request.callsArgWith(1, null, { statusCode: 200 })
|
||||
this.fetchJson.resolves({ statusCode: 200 })
|
||||
this.InstitutionsAPI.getUsersNeedingReconfirmationsLapsedProcessed(
|
||||
error => {
|
||||
expect(error).not.to.exist
|
||||
this.request.calledOnce.should.equal(true)
|
||||
const requestOptions = this.request.lastCall.args[0]
|
||||
this.fetchJson.calledOnce.should.equal(true)
|
||||
const requestOptions = this.fetchJson.lastCall.args[1]
|
||||
const expectedUrl = `v1.url/api/v2/institutions/need_reconfirmation_lapsed_processed`
|
||||
requestOptions.url.should.equal(expectedUrl)
|
||||
this.fetchJson.lastCall.args[0].should.equal(expectedUrl)
|
||||
requestOptions.method.should.equal('GET')
|
||||
done()
|
||||
}
|
||||
|
@ -177,7 +186,7 @@ describe('InstitutionsAPI', function () {
|
|||
})
|
||||
|
||||
it('handle error', function (done) {
|
||||
this.request.callsArgWith(1, null, { statusCode: 500 })
|
||||
this.fetchJson.throws({ info: { statusCode: 500 } })
|
||||
this.InstitutionsAPI.getUsersNeedingReconfirmationsLapsedProcessed(
|
||||
error => {
|
||||
expect(error).to.exist
|
||||
|
@ -189,14 +198,14 @@ describe('InstitutionsAPI', function () {
|
|||
|
||||
describe('addAffiliation', function () {
|
||||
beforeEach(function () {
|
||||
this.request.callsArgWith(1, null, { statusCode: 201 })
|
||||
this.fetchNothing.resolves({ status: 201 })
|
||||
})
|
||||
|
||||
it('add affiliation', function (done) {
|
||||
const affiliationOptions = {
|
||||
university: { id: 1 },
|
||||
role: 'Prof',
|
||||
department: 'Math',
|
||||
role: 'Prof',
|
||||
confirmedAt: new Date(),
|
||||
entitlement: true,
|
||||
}
|
||||
|
@ -206,38 +215,86 @@ describe('InstitutionsAPI', function () {
|
|||
affiliationOptions,
|
||||
err => {
|
||||
expect(err).not.to.exist
|
||||
this.request.calledOnce.should.equal(true)
|
||||
const requestOptions = this.request.lastCall.args[0]
|
||||
this.fetchNothing.calledOnce.should.equal(true)
|
||||
const requestOptions = this.fetchNothing.lastCall.args[1]
|
||||
const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations`
|
||||
requestOptions.url.should.equal(expectedUrl)
|
||||
expect(this.fetchNothing.lastCall.args[0]).to.equal(expectedUrl)
|
||||
requestOptions.method.should.equal('POST')
|
||||
requestOptions.maxAttempts.should.equal(0)
|
||||
|
||||
const { body } = requestOptions
|
||||
Object.keys(body).length.should.equal(7)
|
||||
body.email.should.equal(this.newEmail)
|
||||
body.university.should.equal(affiliationOptions.university)
|
||||
body.department.should.equal(affiliationOptions.department)
|
||||
body.role.should.equal(affiliationOptions.role)
|
||||
body.confirmedAt.should.equal(affiliationOptions.confirmedAt)
|
||||
body.entitlement.should.equal(affiliationOptions.entitlement)
|
||||
const { json } = requestOptions
|
||||
Object.keys(json).length.should.equal(7)
|
||||
expect(json).to.deep.equal(
|
||||
Object.assign(
|
||||
{ email: this.newEmail, rejectIfBlocklisted: undefined },
|
||||
affiliationOptions
|
||||
)
|
||||
)
|
||||
this.markAsReadIpMatcher.calledOnce.should.equal(true)
|
||||
done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('handle error', function (done) {
|
||||
const body = { errors: 'affiliation error message' }
|
||||
this.request.callsArgWith(1, null, { statusCode: 422 }, body)
|
||||
it('handles 422 error', function (done) {
|
||||
const messageFromApi = 'affiliation error message'
|
||||
const body = JSON.stringify({ errors: messageFromApi })
|
||||
this.fetchNothing.throws({ response: { status: 422 }, body })
|
||||
this.InstitutionsAPI.addAffiliation(
|
||||
this.stubbedUser._id,
|
||||
this.newEmail,
|
||||
{},
|
||||
err => {
|
||||
expect(err).to.exist
|
||||
expect(err).to.be.instanceOf(Errors.InvalidInstitutionalEmailError)
|
||||
err.message.should.have.string(422)
|
||||
err.message.should.have.string(body.errors)
|
||||
err.message.should.have.string(messageFromApi)
|
||||
done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('handles 500 error', function (done) {
|
||||
const body = { errors: 'affiliation error message' }
|
||||
this.fetchNothing.throws({ response: { status: 500 }, body })
|
||||
this.InstitutionsAPI.addAffiliation(
|
||||
this.stubbedUser._id,
|
||||
this.newEmail,
|
||||
{},
|
||||
err => {
|
||||
expect(err).to.be.instanceOf(Errors.V1ConnectionError)
|
||||
expect(err.message).to.equal('error getting affiliations from v1')
|
||||
expect(err.info).to.deep.equal({ status: 500, body })
|
||||
done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('uses default error message when no error body in response', function (done) {
|
||||
this.fetchNothing.throws({ response: { status: 429 } })
|
||||
this.InstitutionsAPI.addAffiliation(
|
||||
this.stubbedUser._id,
|
||||
this.newEmail,
|
||||
{},
|
||||
err => {
|
||||
expect(err).to.exist
|
||||
expect(err.message).to.equal("Couldn't create affiliation: 429")
|
||||
done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('does not try to mark IP matcher notifications as read if no university passed', function (done) {
|
||||
const affiliationOptions = {
|
||||
confirmedAt: new Date(),
|
||||
}
|
||||
|
||||
this.InstitutionsAPI.addAffiliation(
|
||||
this.stubbedUser._id,
|
||||
this.newEmail,
|
||||
affiliationOptions,
|
||||
err => {
|
||||
expect(err).not.to.exist
|
||||
expect(this.markAsReadIpMatcher.callCount).to.equal(0)
|
||||
done()
|
||||
}
|
||||
)
|
||||
|
@ -246,7 +303,7 @@ describe('InstitutionsAPI', function () {
|
|||
|
||||
describe('removeAffiliation', function () {
|
||||
beforeEach(function () {
|
||||
this.request.callsArgWith(1, null, { statusCode: 404 })
|
||||
this.fetchNothing.throws({ response: { status: 404 } })
|
||||
})
|
||||
|
||||
it('remove affiliation', function (done) {
|
||||
|
@ -255,19 +312,19 @@ describe('InstitutionsAPI', function () {
|
|||
this.newEmail,
|
||||
err => {
|
||||
expect(err).not.to.exist
|
||||
this.request.calledOnce.should.equal(true)
|
||||
const requestOptions = this.request.lastCall.args[0]
|
||||
this.fetchNothing.calledOnce.should.equal(true)
|
||||
const requestOptions = this.fetchNothing.lastCall.args[1]
|
||||
const expectedUrl = `v1.url/api/v2/users/${this.stubbedUser._id}/affiliations/remove`
|
||||
requestOptions.url.should.equal(expectedUrl)
|
||||
this.fetchNothing.lastCall.args[0].should.equal(expectedUrl)
|
||||
requestOptions.method.should.equal('POST')
|
||||
expect(requestOptions.body).to.deep.equal({ email: this.newEmail })
|
||||
expect(requestOptions.json).to.deep.equal({ email: this.newEmail })
|
||||
done()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('handle error', function (done) {
|
||||
this.request.callsArgWith(1, null, { statusCode: 500 })
|
||||
this.fetchNothing.throws({ response: { status: 500 } })
|
||||
this.InstitutionsAPI.removeAffiliation(
|
||||
this.stubbedUser._id,
|
||||
this.newEmail,
|
||||
|
|
Loading…
Reference in a new issue