mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Refactor POST /db/classroom and PUT /db/campaign/:handle and their tests for #3469
This commit is contained in:
parent
8a744db768
commit
bd6a266f60
10 changed files with 129 additions and 67 deletions
|
@ -7,24 +7,10 @@ mongoose = require 'mongoose'
|
|||
|
||||
CampaignHandler = class CampaignHandler extends Handler
|
||||
modelClass: Campaign
|
||||
editableProperties: [
|
||||
'name'
|
||||
'fullName'
|
||||
'description'
|
||||
'type'
|
||||
'i18n'
|
||||
'i18nCoverage'
|
||||
'ambientSound'
|
||||
'backgroundImage'
|
||||
'backgroundColor'
|
||||
'backgroundColorTransparent'
|
||||
'adjacentCampaigns'
|
||||
'levels'
|
||||
]
|
||||
jsonSchema: require '../../app/schemas/models/campaign.schema'
|
||||
|
||||
hasAccess: (req) ->
|
||||
req.method in ['GET', 'PUT'] or req.user?.isAdmin()
|
||||
req.method in ['GET'] or req.user?.isAdmin()
|
||||
|
||||
hasAccessToDocument: (req, document, method=null) ->
|
||||
return true if req.user?.isAdmin()
|
||||
|
@ -124,10 +110,6 @@ CampaignHandler = class CampaignHandler extends Handler
|
|||
return @sendDatabaseError(res, err) if err
|
||||
return @sendSuccess(res, (achievement.toObject() for achievement in achievements))
|
||||
|
||||
onPutSuccess: (req, doc) ->
|
||||
docLink = "http://codecombat.com#{req.headers['x-current-path']}"
|
||||
@sendChangedSlackMessage creator: req.user, target: doc, docLink: docLink
|
||||
|
||||
getNamesByIDs: (req, res) -> @getNamesByOriginals req, res, true
|
||||
|
||||
module.exports = new CampaignHandler()
|
||||
|
|
|
@ -10,12 +10,11 @@ UserHandler = require './user_handler'
|
|||
ClassroomHandler = class ClassroomHandler extends Handler
|
||||
modelClass: Classroom
|
||||
jsonSchema: require '../../app/schemas/models/classroom.schema'
|
||||
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE']
|
||||
allowedMethods: ['GET', 'PUT', 'DELETE']
|
||||
|
||||
hasAccess: (req) ->
|
||||
return false unless req.user
|
||||
return true if req.method is 'GET'
|
||||
return false if req.method is 'POST' and not req.user?.isTeacher()
|
||||
req.method in @allowedMethods or req.user?.isAdmin()
|
||||
|
||||
hasAccessToDocument: (req, document, method=null) ->
|
||||
|
@ -27,12 +26,6 @@ ClassroomHandler = class ClassroomHandler extends Handler
|
|||
return true if isGet and isMember
|
||||
false
|
||||
|
||||
makeNewInstance: (req) ->
|
||||
instance = super(req)
|
||||
instance.set 'ownerID', req.user._id
|
||||
instance.set 'members', []
|
||||
instance
|
||||
|
||||
getByRelationship: (req, res, args...) ->
|
||||
method = req.method.toLowerCase()
|
||||
return @inviteStudents(req, res, args[0]) if args[1] is 'invite-members'
|
||||
|
|
|
@ -7,6 +7,7 @@ mongoose = require 'mongoose'
|
|||
Campaign = require '../models/Campaign'
|
||||
parse = require '../commons/parse'
|
||||
LevelSession = require '../models/LevelSession'
|
||||
slack = require '../slack'
|
||||
|
||||
module.exports =
|
||||
fetchByType: wrap (req, res, next) ->
|
||||
|
@ -19,3 +20,18 @@ module.exports =
|
|||
campaigns = yield dbq.exec()
|
||||
campaigns = (campaign.toObject({req: req}) for campaign in campaigns)
|
||||
res.status(200).send(campaigns)
|
||||
|
||||
put: wrap (req, res) ->
|
||||
campaign = yield database.getDocFromHandle(req, Campaign)
|
||||
if not campaign
|
||||
throw new errors.NotFound('Campaign not found.')
|
||||
hasPermission = req.user.isAdmin()
|
||||
unless hasPermission or database.isJustFillingTranslations(req, campaign)
|
||||
throw new errors.Forbidden('Must be an admin or submitting translations to edit a campaign')
|
||||
|
||||
database.assignBody(req, campaign)
|
||||
database.validateDoc(campaign)
|
||||
campaign = yield campaign.save()
|
||||
res.status(200).send(campaign.toObject())
|
||||
docLink = "http://codecombat.com#{req.headers['x-current-path']}"
|
||||
slack.sendChangedSlackMessage creator: req.user, target: campaign, docLink: docLink
|
||||
|
|
|
@ -62,3 +62,14 @@ module.exports =
|
|||
memberObjects = (member.toObject({ req: req, includedPrivates: ["name", "email"] }) for member in members)
|
||||
|
||||
res.status(200).send(memberObjects)
|
||||
|
||||
post: wrap (req, res) ->
|
||||
throw new errors.Unauthorized() unless req.user and not req.user.isAnonymous()
|
||||
throw new errors.Forbidden() unless req.user?.isTeacher()
|
||||
classroom = database.initDoc(req, Classroom)
|
||||
classroom.set 'ownerID', req.user._id
|
||||
classroom.set 'members', []
|
||||
database.assignBody(req, classroom)
|
||||
database.validateDoc(classroom)
|
||||
classroom = yield classroom.save()
|
||||
res.status(201).send(classroom.toObject({req: req}))
|
|
@ -37,5 +37,19 @@ CampaignSchema.statics.updateAdjacentCampaigns = (savedCampaign) ->
|
|||
CampaignSchema.post 'save', -> @constructor.updateAdjacentCampaigns @
|
||||
|
||||
CampaignSchema.statics.jsonSchema = jsonSchema
|
||||
CampaignSchema.statics.editableProperties = [
|
||||
'name'
|
||||
'fullName'
|
||||
'description'
|
||||
'type'
|
||||
'i18n'
|
||||
'i18nCoverage'
|
||||
'ambientSound'
|
||||
'backgroundImage'
|
||||
'backgroundColor'
|
||||
'backgroundColorTransparent'
|
||||
'adjacentCampaigns'
|
||||
'levels'
|
||||
]
|
||||
|
||||
module.exports = mongoose.model('campaign', CampaignSchema)
|
||||
|
|
|
@ -54,7 +54,7 @@ ClassroomSchema.set('toObject', {
|
|||
transform: (doc, ret, options) ->
|
||||
return ret unless options.req
|
||||
user = options.req.user
|
||||
unless user?.isAdmin() or user?.get('_id').equals(doc.get('ownerID'))
|
||||
unless user and (user.isAdmin() or user._id.equals(doc.get('ownerID')))
|
||||
delete ret.code
|
||||
delete ret.codeCamel
|
||||
return ret
|
||||
|
|
|
@ -40,7 +40,11 @@ module.exports.setup = (app) ->
|
|||
app.get('/db/article/:handle/patches', mw.patchable.patches(Article))
|
||||
app.post('/db/article/:handle/watchers', mw.patchable.joinWatchers(Article))
|
||||
app.delete('/db/article/:handle/watchers', mw.patchable.leaveWatchers(Article))
|
||||
|
||||
app.get('/db/campaign', mw.campaigns.fetchByType)
|
||||
app.put('/db/campaign/:handle', mw.campaigns.put)
|
||||
|
||||
app.post('/db/classroom', mw.classrooms.post)
|
||||
app.get('/db/classroom', mw.classrooms.getByOwner)
|
||||
app.get('/db/classroom/:handle/member-sessions', mw.classrooms.fetchMemberSessions)
|
||||
app.get('/db/classroom/:handle/members', mw.classrooms.fetchMembers) # TODO: Use mw.auth?
|
||||
|
@ -50,8 +54,6 @@ module.exports.setup = (app) ->
|
|||
app.get('/db/course', mw.rest.get(Course))
|
||||
app.get('/db/course/:handle', mw.rest.getByHandle(Course))
|
||||
|
||||
app.get('/db/campaign', mw.campaigns.fetchByType) #TODO
|
||||
|
||||
app.post('/db/course_instance/:handle/members', mw.auth.checkLoggedIn(), mw.courseInstances.addMembers)
|
||||
|
||||
app.get('/db/user', mw.users.fetchByGPlusID, mw.users.fetchByFacebookID)
|
||||
|
|
|
@ -5,8 +5,13 @@ log = require 'winston'
|
|||
roomChannelMap =
|
||||
main: '#general'
|
||||
artisans: '#artisan'
|
||||
|
||||
module.exports.sendChangedSlackMessage = (options) ->
|
||||
message = "#{options.creator.get('name')} saved a change to #{options.target.get('name')}: #{options.target.get('commitMessage') or '(no commit message)'} #{options.docLink}"
|
||||
rooms = if /Diplomat submission/.test(message) then ['dev-feed'] else ['dev-feed', 'artisans']
|
||||
@sendSlackMessage message, rooms
|
||||
|
||||
module.exports.sendSlackMessage = sendSlackMessage = (message, rooms=['tower'], options={}) ->
|
||||
module.exports.sendSlackMessage = (message, rooms=['tower'], options={}) ->
|
||||
unless config.isProduction
|
||||
log.info "Slack msg: #{message}"
|
||||
return
|
||||
|
|
|
@ -22,6 +22,7 @@ achievement = {
|
|||
campaign = {
|
||||
name: 'Campaign'
|
||||
levels: {}
|
||||
i18n: {}
|
||||
}
|
||||
|
||||
levelURL = getURL('/db/level')
|
||||
|
@ -34,6 +35,46 @@ Campaign = require '../../../server/models/Campaign'
|
|||
Level = require '../../../server/models/Level'
|
||||
User = require '../../../server/models/User'
|
||||
request = require '../request'
|
||||
utils = require '../utils'
|
||||
slack = require '../../../server/slack'
|
||||
|
||||
describe 'PUT /db/campaign', ->
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModels [Achievement, Campaign, Level, User]
|
||||
admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(admin)
|
||||
[res, body] = yield request.postAsync { uri: campaignURL, json: campaign }
|
||||
@campaign = yield Campaign.findById(body._id)
|
||||
done()
|
||||
|
||||
it 'saves changes to campaigns', utils.wrap (done) ->
|
||||
[res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: { name: 'A new name' } }
|
||||
expect(body.name).toBe('A new name')
|
||||
c = yield Campaign.findById(body._id)
|
||||
expect(c.get('name')).toBe('A new name')
|
||||
done()
|
||||
|
||||
it 'does not allow normal users to make changes', utils.wrap (done) ->
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
[res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: { name: 'A new name' } }
|
||||
expect(res.statusCode).toBe(403)
|
||||
done()
|
||||
|
||||
it 'allows normal users to put translation changes', utils.wrap (done) ->
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
json = _.clone @campaign.toObject()
|
||||
json.i18n = { de: { name: 'A new name' } }
|
||||
[res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: json }
|
||||
expect(res.statusCode).toBe(200)
|
||||
done()
|
||||
|
||||
it 'sends a slack message', utils.wrap (done) ->
|
||||
spyOn(slack, 'sendSlackMessage')
|
||||
[res, body] = yield request.putAsync { uri: campaignURL+'/'+@campaign.id, json: { name: 'A new name' } }
|
||||
expect(slack.sendSlackMessage).toHaveBeenCalled()
|
||||
done()
|
||||
|
||||
describe '/db/campaign', ->
|
||||
it 'prepares the db first', (done) ->
|
||||
|
|
|
@ -50,7 +50,7 @@ describe 'GET /db/classroom/:id', ->
|
|||
user1.save (err) ->
|
||||
data = { name: 'Classroom 1' }
|
||||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
classroomID = body._id
|
||||
request.get {uri: classroomsURL + '/' + body._id }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
|
@ -59,36 +59,34 @@ describe 'GET /db/classroom/:id', ->
|
|||
|
||||
describe 'POST /db/classroom', ->
|
||||
|
||||
it 'clears database users and classrooms', (done) ->
|
||||
clearModels [User, Classroom], (err) ->
|
||||
throw err if err
|
||||
done()
|
||||
|
||||
it 'creates a new classroom for the given user', (done) ->
|
||||
loginNewUser (user1) ->
|
||||
user1.set('role', 'teacher')
|
||||
user1.save (err) ->
|
||||
data = { name: 'Classroom 1' }
|
||||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(body.name).toBe('Classroom 1')
|
||||
expect(body.members.length).toBe(0)
|
||||
expect(body.ownerID).toBe(user1.id)
|
||||
done()
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModels [User, Classroom]
|
||||
done()
|
||||
|
||||
it 'creates a new classroom for the given user with teacher role', utils.wrap (done) ->
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 1' }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
expect(res.statusCode).toBe(201)
|
||||
expect(res.body.name).toBe('Classroom 1')
|
||||
expect(res.body.members.length).toBe(0)
|
||||
expect(res.body.ownerID).toBe(teacher.id)
|
||||
done()
|
||||
|
||||
it 'does not work for anonymous users', (done) ->
|
||||
logoutUser ->
|
||||
data = { name: 'Classroom 2' }
|
||||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(401)
|
||||
done()
|
||||
it 'returns 401 for anonymous users', utils.wrap (done) ->
|
||||
data = { name: 'Classroom 2' }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
expect(res.statusCode).toBe(401)
|
||||
done()
|
||||
|
||||
it 'does not work for non-teacher users', (done) ->
|
||||
loginNewUser (user1) ->
|
||||
data = { name: 'Classroom 1' }
|
||||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(403)
|
||||
done()
|
||||
it 'does not work for non-teacher users', utils.wrap (done) ->
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
data = { name: 'Classroom 1' }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
expect(res.statusCode).toBe(403)
|
||||
done()
|
||||
|
||||
|
||||
describe 'PUT /db/classroom', ->
|
||||
|
@ -104,7 +102,7 @@ describe 'PUT /db/classroom', ->
|
|||
user1.save (err) ->
|
||||
data = { name: 'Classroom 2' }
|
||||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
data = { name: 'Classroom 3', description: 'New Description' }
|
||||
url = classroomsURL + '/' + body._id
|
||||
request.put { uri: url, json: data }, (err, res, body) ->
|
||||
|
@ -118,7 +116,7 @@ describe 'PUT /db/classroom', ->
|
|||
user1.save (err) ->
|
||||
data = { name: 'Classroom 4' }
|
||||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
classroomCode = body.code
|
||||
loginNewUser (user2) ->
|
||||
url = getURL("/db/classroom/~/members")
|
||||
|
@ -145,7 +143,7 @@ describe 'POST /db/classroom/~/members', ->
|
|||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
classroomCode = body.code
|
||||
classroomID = body._id
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
loginNewUser (user2) ->
|
||||
url = getURL("/db/classroom/~/members")
|
||||
data = { code: classroomCode }
|
||||
|
@ -166,7 +164,7 @@ describe 'POST /db/classroom/~/members', ->
|
|||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
classroomCode = body.code
|
||||
classroomID = body._id
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
loginNewUser (user2) ->
|
||||
user2.set('role', 'teacher')
|
||||
user2.save (err, user2) ->
|
||||
|
@ -183,7 +181,7 @@ describe 'POST /db/classroom/~/members', ->
|
|||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: { name: 'Classroom' } }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
classroomCode = body.code
|
||||
yield utils.becomeAnonymous()
|
||||
[res, body] = yield request.postAsync { uri: getURL("/db/classroom/~/members"), json: { code: classroomCode } }
|
||||
|
@ -206,7 +204,7 @@ describe 'DELETE /db/classroom/:id/members', ->
|
|||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
classroomCode = body.code
|
||||
classroomID = body._id
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
loginNewUser (user2) ->
|
||||
url = getURL("/db/classroom/~/members")
|
||||
data = { code: classroomCode }
|
||||
|
@ -231,7 +229,7 @@ describe 'POST /db/classroom/:id/invite-members', ->
|
|||
user1.save (err) ->
|
||||
data = { name: 'Classroom 6' }
|
||||
request.post {uri: classroomsURL, json: data }, (err, res, body) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.statusCode).toBe(201)
|
||||
url = classroomsURL + '/' + body._id + '/invite-members'
|
||||
data = { emails: ['test@test.com'] }
|
||||
request.post { uri: url, json: data }, (err, res, body) ->
|
||||
|
|
Loading…
Reference in a new issue