Bunch of server changes, mainly adding all the JSON schema validation and fixing tests.

This commit is contained in:
Scott Erickson 2014-04-08 14:10:50 -07:00
parent 17c1bb7344
commit c382889748
17 changed files with 45 additions and 22 deletions

View file

@ -46,7 +46,7 @@
"mongoose": "3.8.x",
"mongoose-text-search": "~0.0.2",
"request": "2.12.x",
"tv4": "1.0.11",
"tv4": "1.0.x",
"lodash": "~2.0.0",
"underscore.string": "2.3.x",
"async": "0.2.x",
@ -56,7 +56,7 @@
"graceful-fs": "~2.0.1",
"node-force-domain": "~0.1.0",
"mailchimp-api": "2.0.x",
"express-useragent": "~0.0.9",
"express-useragent": "~0.0.9",
"gridfs-stream": "0.4.x",
"stream-buffers": "0.2.x",
"sendwithus": "2.0.x",

View file

@ -4,6 +4,7 @@ Handler = require('../commons/Handler')
ArticleHandler = class ArticleHandler extends Handler
modelClass: Article
editableProperties: ['body', 'name', 'i18n']
jsonSchema: require './article_schema'
hasAccess: (req) ->
req.method is 'GET' or req.user?.isAdmin()

View file

@ -48,6 +48,7 @@ module.exports = class Handler
sendMethodNotAllowed: (res) -> errors.badMethod(res)
sendBadInputError: (res, message) -> errors.badInput(res, message)
sendDatabaseError: (res, err) ->
return @sendError(res, err.code, err.response) if err.response and err.code
log.error "Database error, #{err}"
errors.serverError(res, 'Database error, ' + err)
@ -203,10 +204,9 @@ module.exports = class Handler
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
return @sendBadInputError(res, 'id should not be included.') if req.body._id
return @sendUnauthorizedError(res) unless @hasAccess(req)
validation = @validateDocumentInput(req.body)
return @sendBadInputError(res, validation.errors) unless validation.valid
document = @makeNewInstance(req)
@saveChangesToDocument req, document, (err) =>
return @sendBadInputError(res, err.errors) if err?.valid is false
return @sendDatabaseError(res, err) if err
@sendSuccess(res, @formatEntity(req, document))
@ -220,13 +220,11 @@ module.exports = class Handler
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
return @sendBadInputError(res, 'id should not be included.') if req.body._id
return @sendUnauthorizedError(res) unless @hasAccess(req)
validation = @validateDocumentInput(req.body)
return @sendBadInputError(res, validation.errors) unless validation.valid
document = @makeNewInstance(req)
document.set('original', document._id)
document.set('creator', req.user._id)
@saveChangesToDocument req, document, (err) =>
return @sendBadInputError(res, err.response) if err?.response
return @sendBadInputError(res, err.errors) if err?.valid is false
return @sendDatabaseError(res, err) if err
@sendSuccess(res, @formatEntity(req, document))
@ -245,8 +243,6 @@ module.exports = class Handler
return @sendBadInputError(res, 'This entity is not versioned') unless @modelClass.schema.uses_coco_versions
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
return @sendUnauthorizedError(res) unless @hasAccess(req)
validation = @validateDocumentInput(req.body)
return @sendBadInputError(res, validation.errors) unless validation.valid
@getDocumentForIdOrSlug req.body._id, (err, parentDocument) =>
return @sendBadInputError(res, 'Bad id.') if err and err.name is 'CastError'
return @sendDatabaseError(res, err) if err
@ -261,6 +257,8 @@ module.exports = class Handler
delete updatedObject[prop]
delete updatedObject._id
major = req.body.version?.major
validation = @validateDocumentInput(updatedObject)
return @sendBadInputError(res, validation.errors) unless validation.valid
done = (err, newDocument) =>
return @sendDatabaseError(res, err) if err

View file

@ -6,6 +6,7 @@ module.exports.handlers =
'level_feedback': 'levels/feedbacks/level_feedback_handler'
'level_session': 'levels/sessions/level_session_handler'
'level_system': 'levels/systems/level_system_handler'
'patch': 'patches/patch_handler'
'thang_type': 'levels/thangs/thang_type_handler'
'user': 'users/user_handler'
@ -19,6 +20,7 @@ module.exports.schemas =
'level_session': 'levels/sessions/level_session_schema'
'level_system': 'levels/systems/level_system_schema'
'metaschema': 'commons/metaschema'
'patch': 'patches/patch_schema'
'thang_component': 'levels/thangs/thang_component_schema'
'thang_type': 'levels/thangs/thang_type_schema'
'user': 'users/user_schema'

View file

@ -13,7 +13,7 @@ me.object = (ext, props) -> combine {type: 'object', additionalProperties: false
me.array = (ext, items) -> combine {type: 'array', items: items or {}}, ext
me.shortString = (ext) -> combine({type: 'string', maxLength: 100}, ext)
me.pct = (ext) -> combine({type: 'number', maximum: 1.0, minimum: 0.0}, ext)
me.date = (ext) -> combine({type: 'string', format: 'date-time'}, ext)
me.date = (ext) -> combine({type: ['object', 'string'], format: 'date-time'}, ext)
# should just be string (Mongo ID), but sometimes mongoose turns them into objects representing those, so we are lenient
me.objectId = (ext) -> schema = combine({type: ['object', 'string'] }, ext)
@ -51,7 +51,18 @@ basicProps = (linkFragment) ->
me.extendBasicProperties = (schema, linkFragment) ->
schema.properties = {} unless schema.properties?
_.extend(schema.properties, basicProps(linkFragment))
# PATCHABLE
patchableProps = ->
patches: me.array({title:'Patches'}, {
_id: me.objectId(links: [{rel: "db", href: "/db/patch/{($)}"}], title: "Patch ID", description: "A reference to the patch.")
status: { enum: ['pending', 'accepted', 'rejected', 'cancelled']}
})
me.extendPatchableProperties = (schema) ->
schema.properties = {} unless schema.properties?
_.extend(schema.properties, patchableProps())
# NAMED

View file

@ -3,6 +3,7 @@ Handler = require('../../commons/Handler')
LevelComponentHandler = class LevelComponentHandler extends Handler
modelClass: LevelComponent
jsonSchema: require './level_component_schema'
editableProperties: [
'system'
'description'

View file

@ -4,6 +4,7 @@ Handler = require('../../commons/Handler')
class LevelFeedbackHandler extends Handler
modelClass: LevelFeedback
editableProperties: ['rating', 'review', 'level', 'levelID', 'levelName']
jsonSchema: require './level_feedback_schema'
makeNewInstance: (req) ->
feedback = super(req)

View file

@ -8,6 +8,7 @@ mongoose = require('mongoose')
LevelHandler = class LevelHandler extends Handler
modelClass: Level
jsonSchema: require './level_schema'
editableProperties: [
'description'
'documentation'

View file

@ -9,6 +9,7 @@ class LevelSessionHandler extends Handler
editableProperties: ['multiplayer', 'players', 'code', 'completed', 'state',
'levelName', 'creatorName', 'levelID', 'screenshot',
'chat', 'teamSpells', 'submitted', 'unsubscribed']
jsonSchema: require './level_session_schema'
getByRelationship: (req, res, args...) ->
return @getActiveSessions req, res if args.length is 2 and args[1] is 'active'

View file

@ -13,6 +13,7 @@ LevelSystemHandler = class LevelSystemHandler extends Handler
'configSchema'
]
postEditableProperties: ['name']
jsonSchema: require './level_system_schema'
getEditableProperties: (req, document) ->
props = super(req, document)

View file

@ -3,6 +3,7 @@ Handler = require('../../commons/Handler')
ThangTypeHandler = class ThangTypeHandler extends Handler
modelClass: ThangType
jsonSchema: require './thang_type_schema'
editableProperties: [
'name',
'raw',

View file

@ -42,6 +42,7 @@ module.exports.setup = (app) ->
catch error
log.error("Error trying db method #{req.route.method} route #{parts} from #{name}: #{error}")
log.error(error)
log.error(error.stack)
errors.notFound(res, "Route #{req.path} not found.")
getSchema = (req, res, moduleName) ->
@ -49,7 +50,7 @@ getSchema = (req, res, moduleName) ->
name = schemas[moduleName.replace '.', '_']
schema = require('../' + name)
res.send(schema)
res.send(JSON.stringify(schema, null, '\t'))
res.end()
catch error

View file

@ -78,11 +78,8 @@ unittest.getUser = (email, password, done, force) ->
req = request.post(getURL('/db/user'), (err, response, body) ->
throw err if err
User.findOne({email:email}).exec((err, user) ->
if password is '80yqxpb38j'
user.set('permissions', [ 'admin' ])
user.save (err) ->
wrapUpGetUser(email, user, done)
else
user.set('permissions', if password is '80yqxpb38j' then [ 'admin' ] else [])
user.save (err) ->
wrapUpGetUser(email, user, done)
)
)

View file

@ -55,7 +55,7 @@ describe '/auth/login', ->
it 'rejects wrong passwords', (done) ->
req = request.post(urlLogin, (error, response) ->
expect(response.statusCode).toBe(401)
expect(response.body.indexOf("wrong, wrong")).toBeGreaterThan(-1)
expect(response.body.indexOf("wrong")).toBeGreaterThan(-1)
done()
)
form = req.form()
@ -96,7 +96,6 @@ describe '/auth/reset', ->
it 'resets user password', (done) ->
req = request.post(urlReset, (error, response) ->
expect(response).toBeDefined()
console.log 'status code is', response.statusCode
expect(response.statusCode).toBe(200)
expect(response.body).toBeDefined()
passwordReset = response.body

View file

@ -6,6 +6,9 @@ describe 'Level', ->
name: "King's Peak 3"
description: 'Climb a mountain.'
permissions: simplePermissions
scripts: []
thangs: []
documentation: {specificArticles:[], generalArticles:[]}
urlLevel = '/db/level'

View file

@ -3,11 +3,14 @@ require '../common'
describe 'LevelComponent', ->
component =
name:'Bashes Everything'
name:'BashesEverything'
description:'Makes the unit uncontrollably bash anything bashable, using the bash system.'
code: 'bash();'
language: 'javascript'
language: 'coffeescript'
permissions:simplePermissions
propertyDocumentation: []
system: 'ai'
dependencies: []
components = {}
@ -45,7 +48,7 @@ describe 'LevelComponent', ->
it 'have a unique name.', (done) ->
loginAdmin ->
request.post {uri:url, json:component}, (err, res, body) ->
expect(res.statusCode).toBe(422)
expect(res.statusCode).toBe(409)
done()
it 'can be read by an admin.', (done) ->

View file

@ -11,6 +11,8 @@ describe 'LevelSystem', ->
"""
language: 'coffeescript'
permissions:simplePermissions
dependencies: []
propertyDocumentation: []
systems = {}
@ -48,7 +50,7 @@ describe 'LevelSystem', ->
it 'have a unique name.', (done) ->
loginAdmin ->
request.post {uri:url, json:system}, (err, res, body) ->
expect(res.statusCode).toBe(422)
expect(res.statusCode).toBe(409)
done()
it 'can be read by an admin.', (done) ->