Merge pull request #745 from sharelatex/ta-institution-upgrade-script

Institution Upgrade Script
This commit is contained in:
Timothée Alby 2018-07-26 13:36:09 +02:00 committed by GitHub
commit 46f956bbeb
15 changed files with 120 additions and 32 deletions

View file

@ -3,12 +3,20 @@ metrics = require("metrics-sharelatex")
settings = require "settings-sharelatex" settings = require "settings-sharelatex"
request = require "request" request = require "request"
module.exports = UserAffiliationsManager = module.exports = InstitutionsAPI =
getAffiliations: (userId, callback = (error, body) ->) -> getInstitutionAffiliations: (institutionId, callback = (error, body) ->) ->
makeAffiliationRequest {
method: 'GET'
path: "/api/v2/institutions/#{institutionId.toString()}/affiliations"
defaultErrorMessage: "Couldn't get institution affiliations"
}, callback
getUserAffiliations: (userId, callback = (error, body) ->) ->
makeAffiliationRequest { makeAffiliationRequest {
method: 'GET' method: 'GET'
path: "/api/v2/users/#{userId.toString()}/affiliations" path: "/api/v2/users/#{userId.toString()}/affiliations"
defaultErrorMessage: "Couldn't get affiliations" defaultErrorMessage: "Couldn't get user affiliations"
}, callback }, callback
@ -77,10 +85,11 @@ makeAffiliationRequest = (requestOptions, callback = (error) ->) ->
callback(null, body) callback(null, body)
[ [
'getAffiliations', 'getInstitutionAffiliations'
'getUserAffiliations',
'addAffiliation', 'addAffiliation',
'removeAffiliation', 'removeAffiliation',
].map (method) -> ].map (method) ->
metrics.timeAsyncMethod( metrics.timeAsyncMethod(
UserAffiliationsManager, method, 'mongo.UserAffiliationsManager', logger InstitutionsAPI, method, 'mongo.InstitutionsAPI', logger
) )

View file

@ -0,0 +1,17 @@
logger = require 'logger-sharelatex'
async = require 'async'
db = require("../../infrastructure/mongojs").db
ObjectId = require("../../infrastructure/mongojs").ObjectId
{ getInstitutionAffiliations } = require('./InstitutionsAPI')
FeaturesUpdater = require('../Subscription/FeaturesUpdater')
ASYNC_LIMIT = 10
module.exports = InstitutionsManager =
upgradeInstitutionUsers: (institutionId, callback = (error) ->) ->
getInstitutionAffiliations institutionId, (error, affiliations) ->
return callback(error) if error
async.eachLimit affiliations, ASYNC_LIMIT, refreshFeatures, callback
refreshFeatures = (affiliation, callback) ->
userId = ObjectId(affiliation.user_id)
FeaturesUpdater.refreshFeatures(userId, true, callback)

View file

@ -1,7 +1,7 @@
User = require("../../models/User").User User = require("../../models/User").User
logger = require("logger-sharelatex") logger = require("logger-sharelatex")
metrics = require('metrics-sharelatex') metrics = require('metrics-sharelatex')
{ addAffiliation } = require("./UserAffiliationsManager") { addAffiliation } = require("../Institutions/InstitutionsAPI")
module.exports = UserCreator = module.exports = UserCreator =

View file

@ -4,7 +4,7 @@ ProjectDeleter = require("../Project/ProjectDeleter")
logger = require("logger-sharelatex") logger = require("logger-sharelatex")
SubscriptionHandler = require("../Subscription/SubscriptionHandler") SubscriptionHandler = require("../Subscription/SubscriptionHandler")
async = require("async") async = require("async")
{ deleteAffiliations } = require("./UserAffiliationsManager") { deleteAffiliations } = require("../Institutions/InstitutionsAPI")
module.exports = module.exports =

View file

@ -3,7 +3,7 @@ UserGetter = require("./UserGetter")
UserUpdater = require("./UserUpdater") UserUpdater = require("./UserUpdater")
EmailHelper = require("../Helpers/EmailHelper") EmailHelper = require("../Helpers/EmailHelper")
UserEmailsConfirmationHandler = require "./UserEmailsConfirmationHandler" UserEmailsConfirmationHandler = require "./UserEmailsConfirmationHandler"
{ endorseAffiliation } = require("./UserAffiliationsManager") { endorseAffiliation } = require("../Institutions/InstitutionsAPI")
logger = require("logger-sharelatex") logger = require("logger-sharelatex")
Errors = require "../Errors/Errors" Errors = require "../Errors/Errors"

View file

@ -3,7 +3,7 @@ metrics = require('metrics-sharelatex')
logger = require('logger-sharelatex') logger = require('logger-sharelatex')
db = mongojs.db db = mongojs.db
ObjectId = mongojs.ObjectId ObjectId = mongojs.ObjectId
{ getAffiliations } = require("./UserAffiliationsManager") { getUserAffiliations } = require("../Institutions/InstitutionsAPI")
Errors = require("../Errors/Errors") Errors = require("../Errors/Errors")
module.exports = UserGetter = module.exports = UserGetter =
@ -32,7 +32,7 @@ module.exports = UserGetter =
return callback error if error? return callback error if error?
return callback new Error('User not Found') unless user return callback new Error('User not Found') unless user
getAffiliations userId, (error, affiliationsData) -> getUserAffiliations userId, (error, affiliationsData) ->
return callback error if error? return callback error if error?
callback null, decorateFullEmails(user.email, user.emails, affiliationsData) callback null, decorateFullEmails(user.email, user.emails, affiliationsData)

View file

@ -5,7 +5,7 @@ db = mongojs.db
async = require("async") async = require("async")
ObjectId = mongojs.ObjectId ObjectId = mongojs.ObjectId
UserGetter = require("./UserGetter") UserGetter = require("./UserGetter")
{ addAffiliation, removeAffiliation } = require("./UserAffiliationsManager") { addAffiliation, removeAffiliation } = require("../Institutions/InstitutionsAPI")
FeaturesUpdater = require("../Subscription/FeaturesUpdater") FeaturesUpdater = require("../Subscription/FeaturesUpdater")
EmailHelper = require "../Helpers/EmailHelper" EmailHelper = require "../Helpers/EmailHelper"
Errors = require "../Errors/Errors" Errors = require "../Errors/Errors"

View file

@ -0,0 +1,16 @@
const InstitutionsManager = require(
'../app/js/Features/Institutions/InstitutionsManager'
)
const institutionId = parseInt(process.argv[2])
if (isNaN(institutionId)) throw new Error('No institution id')
console.log('Upgrading users of institution', institutionId)
InstitutionsManager.upgradeInstitutionUsers(institutionId, function (error) {
if (error) {
console.log(error)
} else {
console.log('DONE 👌')
}
process.exit()
})

View file

@ -4,16 +4,16 @@ SandboxedModule = require('sandboxed-module')
assert = require('assert') assert = require('assert')
path = require('path') path = require('path')
sinon = require('sinon') sinon = require('sinon')
modulePath = path.join __dirname, "../../../../app/js/Features/User/UserAffiliationsManager" modulePath = path.join __dirname, "../../../../app/js/Features/Institutions/InstitutionsAPI"
expect = require("chai").expect expect = require("chai").expect
describe "UserAffiliationsManager", -> describe "InstitutionsAPI", ->
beforeEach -> beforeEach ->
@logger = err: sinon.stub(), log: -> @logger = err: sinon.stub(), log: ->
settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } } settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } }
@request = sinon.stub() @request = sinon.stub()
@UserAffiliationsManager = SandboxedModule.require modulePath, requires: @InstitutionsAPI = SandboxedModule.require modulePath, requires:
"logger-sharelatex": @logger "logger-sharelatex": @logger
"metrics-sharelatex": timeAsyncMethod: sinon.stub() "metrics-sharelatex": timeAsyncMethod: sinon.stub()
'settings-sharelatex': settings 'settings-sharelatex': settings
@ -25,11 +25,27 @@ describe "UserAffiliationsManager", ->
email:"hello@world.com" email:"hello@world.com"
@newEmail = "bob@bob.com" @newEmail = "bob@bob.com"
describe 'getAffiliations', -> describe 'getInstitutionAffiliations', ->
it 'get affiliations', (done)->
@institutionId = 123
responseBody = ['123abc', '456def']
@request.yields(null, { statusCode: 200 }, responseBody)
@InstitutionsAPI.getInstitutionAffiliations @institutionId, (err, body) =>
should.not.exist(err)
@request.calledOnce.should.equal true
requestOptions = @request.lastCall.args[0]
expectedUrl = "v1.url/api/v2/institutions/#{@institutionId}/affiliations"
requestOptions.url.should.equal expectedUrl
requestOptions.method.should.equal 'GET'
should.not.exist(requestOptions.body)
body.should.equal responseBody
done()
describe 'getUserAffiliations', ->
it 'get affiliations', (done)-> it 'get affiliations', (done)->
responseBody = [{ foo: 'bar' }] responseBody = [{ foo: 'bar' }]
@request.callsArgWith(1, null, { statusCode: 201 }, responseBody) @request.callsArgWith(1, null, { statusCode: 201 }, responseBody)
@UserAffiliationsManager.getAffiliations @stubbedUser._id, (err, body) => @InstitutionsAPI.getUserAffiliations @stubbedUser._id, (err, body) =>
should.not.exist(err) should.not.exist(err)
@request.calledOnce.should.equal true @request.calledOnce.should.equal true
requestOptions = @request.lastCall.args[0] requestOptions = @request.lastCall.args[0]
@ -43,7 +59,7 @@ describe "UserAffiliationsManager", ->
it 'handle error', (done)-> it 'handle error', (done)->
body = errors: 'affiliation error message' body = errors: 'affiliation error message'
@request.callsArgWith(1, null, { statusCode: 503 }, body) @request.callsArgWith(1, null, { statusCode: 503 }, body)
@UserAffiliationsManager.getAffiliations @stubbedUser._id, (err) => @InstitutionsAPI.getUserAffiliations @stubbedUser._id, (err) =>
should.exist(err) should.exist(err)
err.message.should.have.string 503 err.message.should.have.string 503
err.message.should.have.string body.errors err.message.should.have.string body.errors
@ -58,7 +74,7 @@ describe "UserAffiliationsManager", ->
university: { id: 1 } university: { id: 1 }
role: 'Prof' role: 'Prof'
department: 'Math' department: 'Math'
@UserAffiliationsManager.addAffiliation @stubbedUser._id, @newEmail, affiliationOptions, (err)=> @InstitutionsAPI.addAffiliation @stubbedUser._id, @newEmail, affiliationOptions, (err)=>
should.not.exist(err) should.not.exist(err)
@request.calledOnce.should.equal true @request.calledOnce.should.equal true
requestOptions = @request.lastCall.args[0] requestOptions = @request.lastCall.args[0]
@ -77,7 +93,7 @@ describe "UserAffiliationsManager", ->
it 'handle error', (done)-> it 'handle error', (done)->
body = errors: 'affiliation error message' body = errors: 'affiliation error message'
@request.callsArgWith(1, null, { statusCode: 422 }, body) @request.callsArgWith(1, null, { statusCode: 422 }, body)
@UserAffiliationsManager.addAffiliation @stubbedUser._id, @newEmail, {}, (err)=> @InstitutionsAPI.addAffiliation @stubbedUser._id, @newEmail, {}, (err)=>
should.exist(err) should.exist(err)
err.message.should.have.string 422 err.message.should.have.string 422
err.message.should.have.string body.errors err.message.should.have.string body.errors
@ -88,7 +104,7 @@ describe "UserAffiliationsManager", ->
@request.callsArgWith(1, null, { statusCode: 404 }) @request.callsArgWith(1, null, { statusCode: 404 })
it 'remove affiliation', (done)-> it 'remove affiliation', (done)->
@UserAffiliationsManager.removeAffiliation @stubbedUser._id, @newEmail, (err)=> @InstitutionsAPI.removeAffiliation @stubbedUser._id, @newEmail, (err)=>
should.not.exist(err) should.not.exist(err)
@request.calledOnce.should.equal true @request.calledOnce.should.equal true
requestOptions = @request.lastCall.args[0] requestOptions = @request.lastCall.args[0]
@ -100,7 +116,7 @@ describe "UserAffiliationsManager", ->
it 'handle error', (done)-> it 'handle error', (done)->
@request.callsArgWith(1, null, { statusCode: 500 }) @request.callsArgWith(1, null, { statusCode: 500 })
@UserAffiliationsManager.removeAffiliation @stubbedUser._id, @newEmail, (err)=> @InstitutionsAPI.removeAffiliation @stubbedUser._id, @newEmail, (err)=>
should.exist(err) should.exist(err)
err.message.should.exist err.message.should.exist
done() done()
@ -108,7 +124,7 @@ describe "UserAffiliationsManager", ->
describe 'deleteAffiliations', -> describe 'deleteAffiliations', ->
it 'delete affiliations', (done)-> it 'delete affiliations', (done)->
@request.callsArgWith(1, null, { statusCode: 200 }) @request.callsArgWith(1, null, { statusCode: 200 })
@UserAffiliationsManager.deleteAffiliations @stubbedUser._id, (err) => @InstitutionsAPI.deleteAffiliations @stubbedUser._id, (err) =>
should.not.exist(err) should.not.exist(err)
@request.calledOnce.should.equal true @request.calledOnce.should.equal true
requestOptions = @request.lastCall.args[0] requestOptions = @request.lastCall.args[0]
@ -120,7 +136,7 @@ describe "UserAffiliationsManager", ->
it 'handle error', (done)-> it 'handle error', (done)->
body = errors: 'affiliation error message' body = errors: 'affiliation error message'
@request.callsArgWith(1, null, { statusCode: 518 }, body) @request.callsArgWith(1, null, { statusCode: 518 }, body)
@UserAffiliationsManager.deleteAffiliations @stubbedUser._id, (err) => @InstitutionsAPI.deleteAffiliations @stubbedUser._id, (err) =>
should.exist(err) should.exist(err)
err.message.should.have.string 518 err.message.should.have.string 518
err.message.should.have.string body.errors err.message.should.have.string body.errors
@ -131,7 +147,7 @@ describe "UserAffiliationsManager", ->
@request.callsArgWith(1, null, { statusCode: 204 }) @request.callsArgWith(1, null, { statusCode: 204 })
it 'endorse affiliation', (done)-> it 'endorse affiliation', (done)->
@UserAffiliationsManager.endorseAffiliation @stubbedUser._id, @newEmail, 'Student','Physics', (err)=> @InstitutionsAPI.endorseAffiliation @stubbedUser._id, @newEmail, 'Student','Physics', (err)=>
should.not.exist(err) should.not.exist(err)
@request.calledOnce.should.equal true @request.calledOnce.should.equal true
requestOptions = @request.lastCall.args[0] requestOptions = @request.lastCall.args[0]

View file

@ -0,0 +1,30 @@
should = require('chai').should()
SandboxedModule = require('sandboxed-module')
path = require('path')
sinon = require('sinon')
modulePath = path.join __dirname, "../../../../app/js/Features/Institutions/InstitutionsManager"
describe "InstitutionsManager", ->
beforeEach ->
@institutionId = 123
@logger = log: ->
@getInstitutionAffiliations = sinon.stub()
@refreshFeatures = sinon.stub().yields()
@InstitutionsManager = SandboxedModule.require modulePath, requires:
'logger-sharelatex': @logger
'./InstitutionsAPI':
getInstitutionAffiliations: @getInstitutionAffiliations
'../Subscription/FeaturesUpdater':
refreshFeatures: @refreshFeatures
describe 'upgradeInstitutionUsers', ->
it 'refresh all users Features', (done) ->
affiliations = [
{ user_id: '123abc123abc123abc123abc' }
{ user_id: '456def456def456def456def' }
]
@getInstitutionAffiliations.yields(null, affiliations)
@InstitutionsManager.upgradeInstitutionUsers @institutionId, (error) =>
should.not.exist(error)
sinon.assert.calledTwice(@refreshFeatures)
done()

View file

@ -22,7 +22,7 @@ describe "UserCreator", ->
"../../models/User": User:@UserModel "../../models/User": User:@UserModel
"logger-sharelatex":{log:->} "logger-sharelatex":{log:->}
'metrics-sharelatex': {timeAsyncMethod: ()->} 'metrics-sharelatex': {timeAsyncMethod: ()->}
"./UserAffiliationsManager": addAffiliation: @addAffiliation "../Institutions/InstitutionsAPI": addAffiliation: @addAffiliation
@email = "bob.oswald@gmail.com" @email = "bob.oswald@gmail.com"

View file

@ -31,7 +31,7 @@ describe "UserDeleter", ->
"../Newsletter/NewsletterManager": @NewsletterManager "../Newsletter/NewsletterManager": @NewsletterManager
"../Subscription/SubscriptionHandler": @SubscriptionHandler "../Subscription/SubscriptionHandler": @SubscriptionHandler
"../Project/ProjectDeleter": @ProjectDeleter "../Project/ProjectDeleter": @ProjectDeleter
"./UserAffiliationsManager": "../Institutions/InstitutionsAPI":
deleteAffiliations: @deleteAffiliations deleteAffiliations: @deleteAffiliations
"logger-sharelatex": @logger = { log: sinon.stub() } "logger-sharelatex": @logger = { log: sinon.stub() }

View file

@ -34,7 +34,7 @@ describe "UserEmailsController", ->
"./UserUpdater": @UserUpdater "./UserUpdater": @UserUpdater
"../Helpers/EmailHelper": @EmailHelper "../Helpers/EmailHelper": @EmailHelper
"./UserEmailsConfirmationHandler": @UserEmailsConfirmationHandler = {} "./UserEmailsConfirmationHandler": @UserEmailsConfirmationHandler = {}
"./UserAffiliationsManager": endorseAffiliation: @endorseAffiliation "../Institutions/InstitutionsAPI": endorseAffiliation: @endorseAffiliation
"../Errors/Errors": Errors "../Errors/Errors": Errors
"logger-sharelatex": "logger-sharelatex":
log: -> console.log(arguments) log: -> console.log(arguments)

View file

@ -22,15 +22,15 @@ describe "UserGetter", ->
db: users: findOne: @findOne db: users: findOne: @findOne
ObjectId: (id) -> return id ObjectId: (id) -> return id
settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } } settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } }
@getAffiliations = sinon.stub().callsArgWith(1, null, []) @getUserAffiliations = sinon.stub().callsArgWith(1, null, [])
@UserGetter = SandboxedModule.require modulePath, requires: @UserGetter = SandboxedModule.require modulePath, requires:
"logger-sharelatex": log:-> "logger-sharelatex": log:->
"../../infrastructure/mongojs": @Mongo "../../infrastructure/mongojs": @Mongo
"metrics-sharelatex": timeAsyncMethod: sinon.stub() "metrics-sharelatex": timeAsyncMethod: sinon.stub()
'settings-sharelatex': settings 'settings-sharelatex': settings
'./UserAffiliationsManager': '../Institutions/InstitutionsAPI':
getAffiliations: @getAffiliations getUserAffiliations: @getUserAffiliations
"../Errors/Errors": Errors "../Errors/Errors": Errors
describe "getUser", -> describe "getUser", ->
@ -77,7 +77,7 @@ describe "UserGetter", ->
institution: { name: 'University Name', isUniversity: true } institution: { name: 'University Name', isUniversity: true }
} }
] ]
@getAffiliations.callsArgWith(1, null, affiliationsData) @getUserAffiliations.callsArgWith(1, null, affiliationsData)
@UserGetter.getUserFullEmails @fakeUser._id, (error, fullEmails) => @UserGetter.getUserFullEmails @fakeUser._id, (error, fullEmails) =>
assert.deepEqual fullEmails, [ assert.deepEqual fullEmails, [
{ {

View file

@ -25,7 +25,7 @@ describe "UserUpdater", ->
@UserUpdater = SandboxedModule.require modulePath, requires: @UserUpdater = SandboxedModule.require modulePath, requires:
"logger-sharelatex": @logger "logger-sharelatex": @logger
"./UserGetter": @UserGetter "./UserGetter": @UserGetter
'./UserAffiliationsManager': '../Institutions/InstitutionsAPI':
addAffiliation: @addAffiliation addAffiliation: @addAffiliation
removeAffiliation: @removeAffiliation removeAffiliation: @removeAffiliation
'../Subscription/FeaturesUpdater': refreshFeatures: @refreshFeatures '../Subscription/FeaturesUpdater': refreshFeatures: @refreshFeatures