mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-26 22:13:32 -04:00
Refactor out promisifyAll, use Mongoose's promises and promisify
This commit is contained in:
parent
becad06750
commit
7749f9cc0a
10 changed files with 372 additions and 358 deletions
server
commons
lib
middleware
spec/server
|
@ -3,34 +3,201 @@ winston = require 'winston'
|
|||
mongoose = require 'mongoose'
|
||||
Grid = require 'gridfs-stream'
|
||||
mongooseCache = require 'mongoose-cache'
|
||||
errors = require '../commons/errors'
|
||||
Promise = require 'bluebird'
|
||||
_ = require 'lodash'
|
||||
|
||||
module.exports.connect = () ->
|
||||
address = module.exports.generateMongoConnectionString()
|
||||
winston.info "Connecting to Mongo with connection string #{address}"
|
||||
module.exports =
|
||||
isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24
|
||||
|
||||
connect: () ->
|
||||
address = module.exports.generateMongoConnectionString()
|
||||
winston.info "Connecting to Mongo with connection string #{address}"
|
||||
|
||||
mongoose.connect address
|
||||
mongoose.connection.once 'open', -> Grid.gfs = Grid(mongoose.connection.db, mongoose.mongo)
|
||||
|
||||
# Hack around Mongoose not exporting Aggregate so that we can patch its exec, too
|
||||
# https://github.com/LearnBoost/mongoose/issues/1910
|
||||
Level = require '../levels/Level'
|
||||
Aggregate = Level.aggregate().constructor
|
||||
maxAge = (Math.random() * 10 + 10) * 60 * 1000 # Randomize so that each server doesn't refresh cache from db at same times
|
||||
mongooseCache.install(mongoose, {max: 1000, maxAge: maxAge, debug: false}, Aggregate)
|
||||
|
||||
mongoose.connect address
|
||||
mongoose.connection.once 'open', -> Grid.gfs = Grid(mongoose.connection.db, mongoose.mongo)
|
||||
generateMongoConnectionString: ->
|
||||
if not global.testing and config.tokyo
|
||||
address = config.mongo.mongoose_tokyo_replica_string
|
||||
else if not global.testing and config.saoPaulo
|
||||
address = config.mongo.mongoose_saoPaulo_replica_string
|
||||
else if not global.testing and config.mongo.mongoose_replica_string
|
||||
address = config.mongo.mongoose_replica_string
|
||||
else
|
||||
dbName = config.mongo.db
|
||||
dbName += '_unittest' if global.testing
|
||||
address = config.mongo.host + ':' + config.mongo.port
|
||||
if config.mongo.username and config.mongo.password
|
||||
address = config.mongo.username + ':' + config.mongo.password + '@' + address
|
||||
address = "mongodb://#{address}/#{dbName}"
|
||||
|
||||
return address
|
||||
|
||||
# Hack around Mongoose not exporting Aggregate so that we can patch its exec, too
|
||||
# https://github.com/LearnBoost/mongoose/issues/1910
|
||||
Level = require '../levels/Level'
|
||||
Aggregate = Level.aggregate().constructor
|
||||
maxAge = (Math.random() * 10 + 10) * 60 * 1000 # Randomize so that each server doesn't refresh cache from db at same times
|
||||
mongooseCache.install(mongoose, {max: 1000, maxAge: maxAge, debug: false}, Aggregate)
|
||||
initDoc: (req, Model) ->
|
||||
# TODO: Move to model superclass or plugins?
|
||||
doc = new Model({})
|
||||
|
||||
module.exports.generateMongoConnectionString = ->
|
||||
if not global.testing and config.tokyo
|
||||
address = config.mongo.mongoose_tokyo_replica_string
|
||||
else if not global.testing and config.saoPaulo
|
||||
address = config.mongo.mongoose_saoPaulo_replica_string
|
||||
else if not global.testing and config.mongo.mongoose_replica_string
|
||||
address = config.mongo.mongoose_replica_string
|
||||
else
|
||||
dbName = config.mongo.db
|
||||
dbName += '_unittest' if global.testing
|
||||
address = config.mongo.host + ':' + config.mongo.port
|
||||
if config.mongo.username and config.mongo.password
|
||||
address = config.mongo.username + ':' + config.mongo.password + '@' + address
|
||||
address = "mongodb://#{address}/#{dbName}"
|
||||
if Model.schema.is_patchable
|
||||
watchers = [req.user.get('_id')]
|
||||
if req.user.isAdmin() # https://github.com/codecombat/codecombat/issues/1105
|
||||
nick = mongoose.Types.ObjectId('512ef4805a67a8c507000001')
|
||||
watchers.push nick unless _.find watchers, (id) -> id.equals nick
|
||||
doc.set 'watchers', watchers
|
||||
|
||||
if Model.schema.uses_coco_versions
|
||||
doc.set('original', doc._id)
|
||||
doc.set('creator', req.user._id)
|
||||
|
||||
|
||||
applyCustomSearchToDBQ: (req, dbq) ->
|
||||
specialParameters = ['term', 'project', 'conditions']
|
||||
|
||||
return unless req.user?.isAdmin()
|
||||
return unless req.query.filter or req.query.conditions
|
||||
|
||||
# admins can send any sort of query down the wire
|
||||
# Example URL: http://localhost:3000/db/user?filter[anonymous]=true
|
||||
filter = {}
|
||||
if 'filter' of req.query
|
||||
for own key, val of req.query.filter
|
||||
if key not in specialParameters
|
||||
try
|
||||
filter[key] = JSON.parse(val)
|
||||
catch SyntaxError
|
||||
throw new errors.UnprocessableEntity("Could not parse filter for key '#{key}'.")
|
||||
dbq.find(filter)
|
||||
|
||||
# Conditions are chained query functions, for example: query.find().limit(20).sort('-dateCreated')
|
||||
# Example URL: http://localhost:3000/db/user?conditions[limit]=20&conditions[sort]="-dateCreated"
|
||||
for own key, val of req.query.conditions
|
||||
if not dbq[key]
|
||||
throw new errors.UnprocessableEntity("No query condition '#{key}'.")
|
||||
try
|
||||
val = JSON.parse(val)
|
||||
dbq[key](val)
|
||||
catch SyntaxError
|
||||
throw new errors.UnprocessableEntity("Could not parse condition for key '#{key}'.")
|
||||
|
||||
|
||||
viewSearch: Promise.promisify (dbq, req, done) ->
|
||||
Model = dbq.model
|
||||
# TODO: Make this function only alter dbq or returns a find. It should not also execute the query.
|
||||
term = req.query.term
|
||||
matchedObjects = []
|
||||
filters = if Model.schema.uses_coco_versions or Model.schema.uses_coco_permissions then [filter: {index: true}] else [filter: {}]
|
||||
|
||||
if Model.schema.uses_coco_permissions and req.user
|
||||
filters.push {filter: {index: req.user.get('id')}}
|
||||
|
||||
for filter in filters
|
||||
callback = (err, results) ->
|
||||
return done(new errors.InternalServerError('Error fetching search results.', {err: err})) if err
|
||||
for r in results.results ? results
|
||||
obj = r.obj ? r
|
||||
continue if obj in matchedObjects # TODO: probably need a better equality check
|
||||
matchedObjects.push obj
|
||||
filters.pop() # doesn't matter which one
|
||||
unless filters.length
|
||||
done(null, matchedObjects)
|
||||
|
||||
if term
|
||||
filter.filter.$text = $search: term
|
||||
else if filters.length is 1 and filters[0].filter?.index is true
|
||||
# All we are doing is an empty text search, but that doesn't hit the index,
|
||||
# so we'll just look for the slug.
|
||||
filter.filter = slug: {$exists: true}
|
||||
|
||||
# This try/catch is here to handle when a custom search tries to find by slug. TODO: Fix this more gracefully.
|
||||
try
|
||||
dbq.find filter.filter
|
||||
catch
|
||||
dbq.exec callback
|
||||
|
||||
|
||||
assignBody: (req, doc, options={}) ->
|
||||
if _.isEmpty(req.body)
|
||||
throw new errors.UnprocessableEntity('No input')
|
||||
|
||||
props = doc.schema.editableProperties.slice()
|
||||
|
||||
if doc.isNew
|
||||
props = props.concat doc.schema.postEditableProperties
|
||||
|
||||
if doc.schema.uses_coco_permissions and req.user
|
||||
isOwner = doc.getAccessForUserObjectId(req.user._id) is 'owner'
|
||||
if doc.isNew or isOwner or req.user?.isAdmin()
|
||||
props.push 'permissions'
|
||||
|
||||
props.push 'commitMessage' if doc.schema.uses_coco_versions
|
||||
props.push 'allowPatches' if doc.schema.is_patchable
|
||||
|
||||
for prop in props
|
||||
if (val = req.body[prop])?
|
||||
doc.set prop, val
|
||||
else if options.unsetMissing and doc.get(prop)?
|
||||
doc.set prop, undefined
|
||||
|
||||
|
||||
validateDoc: (doc) ->
|
||||
obj = doc.toObject()
|
||||
# Hack to get saving of Users to work. Probably should replace these props with strings
|
||||
# so that validation doesn't get hung up on Date objects in the documents.
|
||||
delete obj.dateCreated
|
||||
tv4 = require('tv4').tv4
|
||||
result = tv4.validateMultiple(obj, doc.schema.jsonSchema)
|
||||
if not result.valid
|
||||
throw new errors.UnprocessableEntity('JSON-schema validation failed', { validationErrors: result.errors })
|
||||
|
||||
|
||||
getDocFromHandle: Promise.promisify (req, Model, options, done) ->
|
||||
if _.isFunction(options)
|
||||
done = options
|
||||
options = {}
|
||||
|
||||
dbq = Model.find()
|
||||
handle = req.params.handle
|
||||
if not handle
|
||||
return done(new errors.UnprocessableEntity('No handle provided.'))
|
||||
if @isID(handle)
|
||||
dbq.findOne({ _id: handle })
|
||||
else
|
||||
dbq.findOne({ slug: handle })
|
||||
|
||||
dbq.exec(done)
|
||||
|
||||
|
||||
hasAccessToDocument: (req, doc, method) ->
|
||||
method = method or req.method
|
||||
return true if req.user?.isAdmin()
|
||||
|
||||
if doc.schema.uses_coco_translation_coverage and method in ['post', 'put']
|
||||
return true if @isJustFillingTranslations(req, doc)
|
||||
|
||||
if doc.schema.uses_coco_permissions
|
||||
return doc.hasPermissionsForMethod?(req.user, method)
|
||||
return true
|
||||
|
||||
isJustFillingTranslations: (req, doc) ->
|
||||
deltasLib = require '../../app/core/deltas'
|
||||
differ = deltasLib.makeJSONDiffer()
|
||||
omissions = ['original'].concat(deltasLib.DOC_SKIP_PATHS)
|
||||
delta = differ.diff(_.omit(doc.toObject(), omissions), _.omit(req.body, omissions))
|
||||
flattened = deltasLib.flattenDelta(delta)
|
||||
_.all flattened, (delta) ->
|
||||
# sometimes coverage gets moved around... allow other changes to happen to i18nCoverage
|
||||
return false unless _.isArray(delta.o)
|
||||
return true if 'i18nCoverage' in delta.dataPath
|
||||
return false unless delta.o.length is 1
|
||||
index = delta.deltaPath.indexOf('i18n')
|
||||
return false if index is -1
|
||||
return false if delta.deltaPath[index+1] in ['en', 'en-US', 'en-GB'] # English speakers are most likely just spamming, so always treat those as patches, not saves.
|
||||
return true
|
||||
|
||||
return address
|
||||
|
|
60
server/commons/parse.coffee
Normal file
60
server/commons/parse.coffee
Normal file
|
@ -0,0 +1,60 @@
|
|||
errors = require '../commons/errors'
|
||||
_ = require 'lodash'
|
||||
Promise = require 'bluebird'
|
||||
|
||||
module.exports =
|
||||
|
||||
getLimitFromReq: (req, options) ->
|
||||
options = _.extend({
|
||||
max: 1000
|
||||
default: 100
|
||||
}, options)
|
||||
|
||||
limit = options.default
|
||||
|
||||
if req.query.limit
|
||||
limit = parseInt(req.query.limit)
|
||||
valid = tv4.validate(limit, {
|
||||
type: 'integer'
|
||||
maximum: options.max
|
||||
minimum: 1
|
||||
})
|
||||
if not valid
|
||||
throw new errors.UnprocessableEntity('Invalid limit parameter.')
|
||||
|
||||
return limit
|
||||
|
||||
|
||||
getSkipFromReq: (req, options) ->
|
||||
options = _.extend({
|
||||
max: 1000000
|
||||
default: 0
|
||||
}, options)
|
||||
|
||||
skip = options.default
|
||||
|
||||
if req.query.skip
|
||||
skip = parseInt(req.query.skip)
|
||||
valid = tv4.validate(skip, {
|
||||
type: 'integer'
|
||||
maximum: options.max
|
||||
minimum: 0
|
||||
})
|
||||
if not valid
|
||||
throw new errors.UnprocessableEntity('Invalid sort parameter.')
|
||||
|
||||
return skip
|
||||
|
||||
|
||||
getProjectFromReq: (req, options) ->
|
||||
options = _.extend({}, options)
|
||||
return null unless req.query.project
|
||||
projection = {}
|
||||
|
||||
if req.query.project is 'true'
|
||||
projection = {original: 1, name: 1, version: 1, description: 1, slug: 1, kind: 1, created: 1, permissions: 1}
|
||||
else
|
||||
for field in req.query.project.split(',')
|
||||
projection[field] = 1
|
||||
|
||||
return projection
|
|
@ -2,12 +2,10 @@ AnalyticsString = require '../analytics/AnalyticsString'
|
|||
log = require 'winston'
|
||||
mongoose = require 'mongoose'
|
||||
config = require '../../server_config'
|
||||
errors = require '../commons/errors'
|
||||
_ = require 'lodash'
|
||||
Promise = require 'bluebird'
|
||||
deltasLib = require '../../app/core/deltas'
|
||||
|
||||
module.exports = utils =
|
||||
# TODO: Remove, use commons/database.isID instead
|
||||
isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24
|
||||
|
||||
getCodeCamel: (numWords=3) ->
|
||||
|
@ -96,219 +94,3 @@ module.exports = utils =
|
|||
@analyticsStringCache[str] = document._id
|
||||
return callback @analyticsStringCache[str]
|
||||
insertString()
|
||||
|
||||
getLimitFromReq: (req, options) ->
|
||||
options = _.extend({
|
||||
max: 1000
|
||||
default: 100
|
||||
}, options)
|
||||
|
||||
limit = options.default
|
||||
|
||||
if req.query.limit
|
||||
limit = parseInt(req.query.limit)
|
||||
valid = tv4.validate(limit, {
|
||||
type: 'integer'
|
||||
maximum: options.max
|
||||
minimum: 1
|
||||
})
|
||||
if not valid
|
||||
throw new errors.UnprocessableEntity('Invalid limit parameter.')
|
||||
|
||||
return limit
|
||||
|
||||
|
||||
getSkipFromReq: (req, options) ->
|
||||
options = _.extend({
|
||||
max: 1000000
|
||||
default: 0
|
||||
}, options)
|
||||
|
||||
skip = options.default
|
||||
|
||||
if req.query.skip
|
||||
skip = parseInt(req.query.skip)
|
||||
valid = tv4.validate(skip, {
|
||||
type: 'integer'
|
||||
maximum: options.max
|
||||
minimum: 0
|
||||
})
|
||||
if not valid
|
||||
throw new errors.UnprocessableEntity('Invalid sort parameter.')
|
||||
|
||||
return skip
|
||||
|
||||
|
||||
getProjectFromReq: (req, options) ->
|
||||
options = _.extend({}, options)
|
||||
return null unless req.query.project
|
||||
projection = {}
|
||||
|
||||
if req.query.project is 'true'
|
||||
projection = {original: 1, name: 1, version: 1, description: 1, slug: 1, kind: 1, created: 1, permissions: 1}
|
||||
else
|
||||
for field in req.query.project.split(',')
|
||||
projection[field] = 1
|
||||
|
||||
return projection
|
||||
|
||||
|
||||
applyCustomSearchToDBQ: (req, dbq) ->
|
||||
specialParameters = ['term', 'project', 'conditions']
|
||||
|
||||
return unless req.user?.isAdmin()
|
||||
return unless req.query.filter or req.query.conditions
|
||||
|
||||
# admins can send any sort of query down the wire
|
||||
# Example URL: http://localhost:3000/db/user?filter[anonymous]=true
|
||||
filter = {}
|
||||
if 'filter' of req.query
|
||||
for own key, val of req.query.filter
|
||||
if key not in specialParameters
|
||||
try
|
||||
filter[key] = JSON.parse(val)
|
||||
catch SyntaxError
|
||||
throw new errors.UnprocessableEntity("Could not parse filter for key '#{key}'.")
|
||||
dbq.find(filter)
|
||||
|
||||
# Conditions are chained query functions, for example: query.find().limit(20).sort('-dateCreated')
|
||||
# Example URL: http://localhost:3000/db/user?conditions[limit]=20&conditions[sort]="-dateCreated"
|
||||
for own key, val of req.query.conditions
|
||||
if not dbq[key]
|
||||
throw new errors.UnprocessableEntity("No query condition '#{key}'.")
|
||||
try
|
||||
val = JSON.parse(val)
|
||||
dbq[key](val)
|
||||
catch SyntaxError
|
||||
throw new errors.UnprocessableEntity("Could not parse condition for key '#{key}'.")
|
||||
|
||||
|
||||
viewSearch: (dbq, req, done) ->
|
||||
Model = dbq.model
|
||||
# TODO: Make this function only alter dbq or returns a find. It should not also execute the query.
|
||||
term = req.query.term
|
||||
matchedObjects = []
|
||||
filters = if Model.schema.uses_coco_versions or Model.schema.uses_coco_permissions then [filter: {index: true}] else [filter: {}]
|
||||
|
||||
if Model.schema.uses_coco_permissions and req.user
|
||||
filters.push {filter: {index: req.user.get('id')}}
|
||||
|
||||
for filter in filters
|
||||
callback = (err, results) ->
|
||||
return done(new errors.InternalServerError('Error fetching search results.', {err: err})) if err
|
||||
for r in results.results ? results
|
||||
obj = r.obj ? r
|
||||
continue if obj in matchedObjects # TODO: probably need a better equality check
|
||||
matchedObjects.push obj
|
||||
filters.pop() # doesn't matter which one
|
||||
unless filters.length
|
||||
done(null, matchedObjects)
|
||||
|
||||
if term
|
||||
filter.filter.$text = $search: term
|
||||
else if filters.length is 1 and filters[0].filter?.index is true
|
||||
# All we are doing is an empty text search, but that doesn't hit the index,
|
||||
# so we'll just look for the slug.
|
||||
filter.filter = slug: {$exists: true}
|
||||
|
||||
# This try/catch is here to handle when a custom search tries to find by slug. TODO: Fix this more gracefully.
|
||||
try
|
||||
dbq.find filter.filter
|
||||
catch
|
||||
dbq.exec callback
|
||||
|
||||
|
||||
assignBody: (req, doc, options={}) ->
|
||||
if _.isEmpty(req.body)
|
||||
throw new errors.UnprocessableEntity('No input')
|
||||
|
||||
props = doc.schema.editableProperties.slice()
|
||||
|
||||
if doc.isNew
|
||||
props = props.concat doc.schema.postEditableProperties
|
||||
|
||||
if doc.schema.uses_coco_permissions and req.user
|
||||
isOwner = doc.getAccessForUserObjectId(req.user._id) is 'owner'
|
||||
if doc.isNew or isOwner or req.user?.isAdmin()
|
||||
props.push 'permissions'
|
||||
|
||||
props.push 'commitMessage' if doc.schema.uses_coco_versions
|
||||
props.push 'allowPatches' if doc.schema.is_patchable
|
||||
|
||||
for prop in props
|
||||
if (val = req.body[prop])?
|
||||
doc.set prop, val
|
||||
else if options.unsetMissing and doc.get(prop)?
|
||||
doc.set prop, undefined
|
||||
|
||||
|
||||
validateDoc: (doc) ->
|
||||
obj = doc.toObject()
|
||||
# Hack to get saving of Users to work. Probably should replace these props with strings
|
||||
# so that validation doesn't get hung up on Date objects in the documents.
|
||||
delete obj.dateCreated
|
||||
tv4 = require('tv4').tv4
|
||||
result = tv4.validateMultiple(obj, doc.schema.jsonSchema)
|
||||
if not result.valid
|
||||
throw new errors.UnprocessableEntity('JSON-schema validation failed', { validationErrors: result.errors })
|
||||
|
||||
|
||||
getDocFromHandle: (req, Model, options, done) ->
|
||||
if _.isFunction(options)
|
||||
done = options
|
||||
options = {}
|
||||
|
||||
dbq = Model.find()
|
||||
handle = req.params.handle
|
||||
if not handle
|
||||
return done(new errors.UnprocessableEntity('No handle provided.'))
|
||||
if utils.isID(handle)
|
||||
dbq.findOne({ _id: handle })
|
||||
else
|
||||
dbq.findOne({ slug: handle })
|
||||
|
||||
dbq.exec(done)
|
||||
|
||||
|
||||
initDoc: (req, Model) ->
|
||||
# TODO: Move to model superclass or plugins?
|
||||
doc = new Model({})
|
||||
|
||||
if Model.schema.is_patchable
|
||||
watchers = [req.user.get('_id')]
|
||||
if req.user.isAdmin() # https://github.com/codecombat/codecombat/issues/1105
|
||||
nick = mongoose.Types.ObjectId('512ef4805a67a8c507000001')
|
||||
watchers.push nick unless _.find watchers, (id) -> id.equals nick
|
||||
doc.set 'watchers', watchers
|
||||
|
||||
if Model.schema.uses_coco_versions
|
||||
doc.set('original', doc._id)
|
||||
doc.set('creator', req.user._id)
|
||||
|
||||
hasAccessToDocument: (req, doc, method) ->
|
||||
method = method or req.method
|
||||
return true if req.user?.isAdmin()
|
||||
|
||||
if doc.schema.uses_coco_translation_coverage and method in ['post', 'put']
|
||||
return true if @isJustFillingTranslations(req, doc)
|
||||
|
||||
if doc.schema.uses_coco_permissions
|
||||
return doc.hasPermissionsForMethod?(req.user, method)
|
||||
return true
|
||||
|
||||
isJustFillingTranslations: (req, doc) ->
|
||||
differ = deltasLib.makeJSONDiffer()
|
||||
omissions = ['original'].concat(deltasLib.DOC_SKIP_PATHS)
|
||||
delta = differ.diff(_.omit(doc.toObject(), omissions), _.omit(req.body, omissions))
|
||||
flattened = deltasLib.flattenDelta(delta)
|
||||
_.all flattened, (delta) ->
|
||||
# sometimes coverage gets moved around... allow other changes to happen to i18nCoverage
|
||||
return false unless _.isArray(delta.o)
|
||||
return true if 'i18nCoverage' in delta.dataPath
|
||||
return false unless delta.o.length is 1
|
||||
index = delta.deltaPath.indexOf('i18n')
|
||||
return false if index is -1
|
||||
return false if delta.deltaPath[index+1] in ['en', 'en-US', 'en-GB'] # English speakers are most likely just spamming, so always treat those as patches, not saves.
|
||||
return true
|
||||
|
||||
Promise.promisifyAll(module.exports)
|
|
@ -3,10 +3,11 @@ errors = require '../commons/errors'
|
|||
wrap = require 'co-express'
|
||||
Grid = require 'gridfs-stream'
|
||||
Promise = require 'bluebird'
|
||||
database = require '../commons/database'
|
||||
|
||||
module.exports =
|
||||
files: (Model, options={}) -> wrap (req, res) ->
|
||||
doc = yield utils.getDocFromHandleAsync(req, Model)
|
||||
doc = yield database.getDocFromHandle(req, Model)
|
||||
if not doc
|
||||
throw new errors.NotFound('Document not found.')
|
||||
module = options.module or req.path[4..].split('/')[0]
|
||||
|
|
|
@ -2,6 +2,7 @@ utils = require '../lib/utils'
|
|||
errors = require '../commons/errors'
|
||||
wrap = require 'co-express'
|
||||
Promise = require 'bluebird'
|
||||
database = require '../commons/database'
|
||||
|
||||
module.exports =
|
||||
names: (Model, options={}) -> wrap (req, res) ->
|
||||
|
@ -17,7 +18,7 @@ module.exports =
|
|||
sort = if Model.schema.uses_coco_versions then {'version.major': -1, 'version.minor': -1} else {}
|
||||
|
||||
for id in ids
|
||||
if not utils.isID(id)
|
||||
if not database.isID(id)
|
||||
throw new errors.UnprocessableEntity('Invalid MongoDB id given')
|
||||
|
||||
ids = (mongoose.Types.ObjectId(id) for id in ids)
|
||||
|
|
|
@ -4,16 +4,18 @@ wrap = require 'co-express'
|
|||
Promise = require 'bluebird'
|
||||
Patch = require '../models/Patch'
|
||||
mongoose = require 'mongoose'
|
||||
database = require '../commons/database'
|
||||
parse = require '../commons/parse'
|
||||
|
||||
module.exports =
|
||||
patches: (options={}) -> wrap (req, res) ->
|
||||
dbq = Patch.find()
|
||||
dbq.limit(utils.getLimitFromReq(req))
|
||||
dbq.skip(utils.getSkipFromReq(req))
|
||||
dbq.select(utils.getProjectFromReq(req))
|
||||
dbq.limit(parse.getLimitFromReq(req))
|
||||
dbq.skip(parse.getSkipFromReq(req))
|
||||
dbq.select(parse.getProjectFromReq(req))
|
||||
|
||||
id = req.params.handle
|
||||
if not utils.isID(id)
|
||||
if not database.isID(id)
|
||||
throw new errors.UnprocessableEntity('Invalid ID')
|
||||
|
||||
query =
|
||||
|
@ -27,10 +29,10 @@ module.exports =
|
|||
res.status(200).send(patches)
|
||||
|
||||
joinWatchers: (Model, options={}) -> wrap (req, res) ->
|
||||
doc = yield utils.getDocFromHandleAsync(req, Model)
|
||||
doc = yield database.getDocFromHandle(req, Model)
|
||||
if not doc
|
||||
throw new errors.NotFound('Document not found.')
|
||||
if not utils.hasAccessToDocument(req, doc, 'get')
|
||||
if not database.hasAccessToDocument(req, doc, 'get')
|
||||
throw new errors.Forbidden()
|
||||
updateResult = yield doc.update({ $addToSet: { watchers: req.user.get('_id') }})
|
||||
if updateResult.nModified
|
||||
|
@ -40,7 +42,7 @@ module.exports =
|
|||
res.status(200).send(doc)
|
||||
|
||||
leaveWatchers: (Model, options={}) -> wrap (req, res) ->
|
||||
doc = yield utils.getDocFromHandleAsync(req, Model)
|
||||
doc = yield database.getDocFromHandle(req, Model)
|
||||
if not doc
|
||||
throw new errors.NotFound('Document not found.')
|
||||
updateResult = yield doc.update({ $pull: { watchers: req.user.get('_id') }})
|
||||
|
|
|
@ -1,40 +1,42 @@
|
|||
utils = require '../lib/utils'
|
||||
errors = require '../commons/errors'
|
||||
wrap = require 'co-express'
|
||||
database = require '../commons/database'
|
||||
parse = require '../commons/parse'
|
||||
|
||||
module.exports =
|
||||
get: (Model, options={}) -> wrap (req, res) ->
|
||||
dbq = Model.find()
|
||||
dbq.limit(utils.getLimitFromReq(req))
|
||||
dbq.skip(utils.getSkipFromReq(req))
|
||||
dbq.select(utils.getProjectFromReq(req))
|
||||
utils.applyCustomSearchToDBQ(req, dbq)
|
||||
dbq.limit(parse.getLimitFromReq(req))
|
||||
dbq.skip(parse.getSkipFromReq(req))
|
||||
dbq.select(parse.getProjectFromReq(req))
|
||||
database.applyCustomSearchToDBQ(req, dbq)
|
||||
|
||||
if Model.schema.uses_coco_translation_coverage and req.query.view is 'i18n-coverage'
|
||||
dbq.find({ slug: {$exists: true}, i18nCoverage: {$exists: true} })
|
||||
|
||||
results = yield utils.viewSearchAsync(dbq, req)
|
||||
results = yield database.viewSearch(dbq, req)
|
||||
res.send(results)
|
||||
|
||||
post: (Model, options={}) -> wrap (req, res) ->
|
||||
doc = utils.initDoc(req, Model)
|
||||
utils.assignBody(req, doc)
|
||||
utils.validateDoc(doc)
|
||||
doc = database.initDoc(req, Model)
|
||||
database.assignBody(req, doc)
|
||||
database.validateDoc(doc)
|
||||
doc = yield doc.save()
|
||||
res.status(201).send(doc.toObject())
|
||||
|
||||
getByHandle: (Model, options={}) -> wrap (req, res) ->
|
||||
doc = yield utils.getDocFromHandleAsync(req, Model)
|
||||
doc = yield database.getDocFromHandle(req, Model)
|
||||
if not doc
|
||||
throw new errors.NotFound('Document not found.')
|
||||
res.status(200).send(doc.toObject())
|
||||
|
||||
put: (Model, options={}) -> wrap (req, res) ->
|
||||
doc = yield utils.getDocFromHandleAsync(req, Model)
|
||||
doc = yield database.getDocFromHandle(req, Model)
|
||||
if not doc
|
||||
throw new errors.NotFound('Document not found.')
|
||||
|
||||
utils.assignBody(req, doc)
|
||||
utils.validateDoc(doc)
|
||||
|
||||
database.assignBody(req, doc)
|
||||
database.validateDoc(doc)
|
||||
doc = yield doc.save()
|
||||
res.status(200).send(doc.toObject())
|
||||
|
|
|
@ -6,10 +6,12 @@ hipchat = require '../hipchat'
|
|||
_ = require 'lodash'
|
||||
wrap = require 'co-express'
|
||||
mongoose = require 'mongoose'
|
||||
database = require '../commons/database'
|
||||
parse = require '../commons/parse'
|
||||
|
||||
module.exports =
|
||||
postNewVersion: (Model, options={}) -> wrap (req, res) ->
|
||||
parent = yield utils.getDocFromHandleAsync(req, Model)
|
||||
parent = yield database.getDocFromHandle(req, Model)
|
||||
if not parent
|
||||
throw new errors.NotFound('Parent not found.')
|
||||
|
||||
|
@ -19,14 +21,14 @@ module.exports =
|
|||
permissions = [permissions] if _.isString(permissions)
|
||||
permissions = ['admin'] if not _.isArray(permissions)
|
||||
hasPermission = _.any(req.user?.hasPermission(permission) for permission in permissions)
|
||||
if not (hasPermission or utils.isJustFillingTranslations(req, parent))
|
||||
if not (hasPermission or database.isJustFillingTranslations(req, parent))
|
||||
throw new errors.Forbidden()
|
||||
|
||||
doc = utils.initDoc(req, Model)
|
||||
doc = database.initDoc(req, Model)
|
||||
ATTRIBUTES_NOT_INHERITED = ['_id', 'version', 'created', 'creator']
|
||||
doc.set(_.omit(parent.toObject(), ATTRIBUTES_NOT_INHERITED))
|
||||
|
||||
utils.assignBody(req, doc, { unsetMissing: true })
|
||||
database.assignBody(req, doc, { unsetMissing: true })
|
||||
|
||||
# Get latest version
|
||||
major = req.body.version?.major
|
||||
|
@ -124,7 +126,7 @@ module.exports =
|
|||
# can get latest overall version, latest of a major version, or a specific version
|
||||
original = req.params.handle
|
||||
version = req.params.version
|
||||
if not utils.isID(original)
|
||||
if not database.isID(original)
|
||||
throw new errors.UnprocessableEntity('Invalid MongoDB id: '+original)
|
||||
|
||||
query = { 'original': mongoose.Types.ObjectId(original) }
|
||||
|
@ -139,7 +141,7 @@ module.exports =
|
|||
dbq.sort({ 'version.major': -1, 'version.minor': -1 })
|
||||
|
||||
# Make sure that permissions and version are fetched, but not sent back if they didn't ask for them.
|
||||
projection = utils.getProjectFromReq(req)
|
||||
projection = parse.getProjectFromReq(req)
|
||||
if projection
|
||||
extraProjectionProps = []
|
||||
extraProjectionProps.push 'permissions' unless projection.permissions
|
||||
|
@ -150,7 +152,7 @@ module.exports =
|
|||
|
||||
doc = yield dbq.exec()
|
||||
throw new errors.NotFound() if not doc
|
||||
throw new errors.Forbidden() unless utils.hasAccessToDocument(req, doc)
|
||||
throw new errors.Forbidden() unless database.hasAccessToDocument(req, doc)
|
||||
doc = _.omit doc, extraProjectionProps if extraProjectionProps?
|
||||
|
||||
res.status(200).send(doc.toObject())
|
||||
|
@ -160,9 +162,9 @@ module.exports =
|
|||
original = req.params.handle
|
||||
dbq = Model.find({'original': mongoose.Types.ObjectId(original)})
|
||||
dbq.sort({'created': -1})
|
||||
dbq.limit(utils.getLimitFromReq(req))
|
||||
dbq.skip(utils.getSkipFromReq(req))
|
||||
dbq.select(utils.getProjectFromReq(req) or 'slug name version commitMessage created creator permissions')
|
||||
dbq.limit(parse.getLimitFromReq(req))
|
||||
dbq.skip(parse.getSkipFromReq(req))
|
||||
dbq.select(parse.getProjectFromReq(req) or 'slug name version commitMessage created creator permissions')
|
||||
|
||||
results = yield dbq.exec()
|
||||
res.status(200).send(results)
|
||||
|
|
|
@ -9,12 +9,12 @@ describe 'GET /db/article', ->
|
|||
articleData2 = { name: 'Article 2', body: 'Article 2 body moo' }
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
@admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.clearModels([Article])
|
||||
@admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(@admin)
|
||||
yield request.postAsync(getURL('/db/article'), { json: articleData1 })
|
||||
yield request.postAsync(getURL('/db/article'), { json: articleData2 })
|
||||
yield utils.logoutAsync()
|
||||
yield utils.logout()
|
||||
done()
|
||||
|
||||
|
||||
|
@ -68,22 +68,22 @@ describe 'GET /db/article', ->
|
|||
|
||||
|
||||
it 'accepts custom filter parameters', utils.wrap (done) ->
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.loginUser(@admin)
|
||||
[res, body] = yield request.getAsync {uri: getURL('/db/article?filter[slug]="article-1"'), json: true}
|
||||
expect(body.length).toBe(1)
|
||||
done()
|
||||
|
||||
|
||||
it 'ignores custom filter parameters for non-admins', utils.wrap (done) ->
|
||||
user = yield utils.initUserAsync()
|
||||
yield utils.loginUserAsync(user)
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
[res, body] = yield request.getAsync {uri: getURL('/db/article?filter[slug]="article-1"'), json: true}
|
||||
expect(body.length).toBe(2)
|
||||
done()
|
||||
|
||||
|
||||
it 'accepts custom condition parameters', utils.wrap (done) ->
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.loginUser(@admin)
|
||||
[res, body] = yield request.getAsync {uri: getURL('/db/article?conditions[select]="slug body"'), json: true}
|
||||
expect(body.length).toBe(2)
|
||||
for doc in body
|
||||
|
@ -92,8 +92,8 @@ describe 'GET /db/article', ->
|
|||
|
||||
|
||||
it 'ignores custom condition parameters for non-admins', utils.wrap (done) ->
|
||||
user = yield utils.initUserAsync()
|
||||
yield utils.loginUserAsync(user)
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
[res, body] = yield request.getAsync {uri: getURL('/db/article?conditions[select]="slug body"'), json: true}
|
||||
expect(body.length).toBe(2)
|
||||
for doc in body
|
||||
|
@ -120,9 +120,9 @@ describe 'POST /db/article', ->
|
|||
articleData = { name: 'Article', body: 'Article', otherProp: 'not getting set' }
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
@admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.clearModels([Article])
|
||||
@admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(@admin)
|
||||
[@res, @body] = yield request.postAsync {
|
||||
uri: getURL('/db/article'), json: articleData
|
||||
}
|
||||
|
@ -172,26 +172,26 @@ describe 'POST /db/article', ->
|
|||
|
||||
|
||||
it 'allows artisans to create Articles', utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
artisan = yield utils.initArtisanAsync({})
|
||||
yield utils.loginUserAsync(artisan)
|
||||
yield utils.clearModels([Article])
|
||||
artisan = yield utils.initArtisan({})
|
||||
yield utils.loginUser(artisan)
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/article'), json: articleData })
|
||||
expect(res.statusCode).toBe(201)
|
||||
done()
|
||||
|
||||
|
||||
it 'does not allow normal users to create Articles', utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
user = yield utils.initUserAsync({})
|
||||
yield utils.loginUserAsync(user)
|
||||
yield utils.clearModels([Article])
|
||||
user = yield utils.initUser({})
|
||||
yield utils.loginUser(user)
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/article'), json: articleData })
|
||||
expect(res.statusCode).toBe(403)
|
||||
done()
|
||||
|
||||
|
||||
it 'does not allow anonymous users to create Articles', utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
yield utils.logoutAsync()
|
||||
yield utils.clearModels([Article])
|
||||
yield utils.logout()
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/article'), json: articleData })
|
||||
expect(res.statusCode).toBe(401)
|
||||
done()
|
||||
|
@ -214,9 +214,9 @@ describe 'GET /db/article/:handle', ->
|
|||
articleData = { name: 'Some Name', body: 'Article' }
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
@admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.clearModels([Article])
|
||||
@admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(@admin)
|
||||
[@res, @body] = yield request.postAsync {
|
||||
uri: getURL('/db/article'), json: articleData
|
||||
}
|
||||
|
@ -247,9 +247,9 @@ putTests = (method='PUT') ->
|
|||
articleData = { name: 'Some Name', body: 'Article' }
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
@admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.clearModels([Article])
|
||||
@admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(@admin)
|
||||
[@res, @body] = yield request.postAsync {
|
||||
uri: getURL('/db/article'), json: articleData
|
||||
}
|
||||
|
@ -270,8 +270,8 @@ putTests = (method='PUT') ->
|
|||
|
||||
|
||||
it 'does not allow normal artisan, non-admins to make changes', utils.wrap (done) ->
|
||||
artisan = yield utils.initArtisanAsync({})
|
||||
yield utils.loginUserAsync(artisan)
|
||||
artisan = yield utils.initArtisan({})
|
||||
yield utils.loginUser(artisan)
|
||||
[res, body] = yield requestAsync {method: method, uri: getURL("/db/article/#{@body._id}"), json: { name: 'Another name' }}
|
||||
expect(res.statusCode).toBe(403)
|
||||
done()
|
||||
|
@ -286,9 +286,9 @@ describe 'POST /db/article/:handle/new-version', ->
|
|||
articleID = null
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
@admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.clearModels([Article])
|
||||
@admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(@admin)
|
||||
[res, body] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
|
||||
expect(res.statusCode).toBe(201)
|
||||
articleID = body._id
|
||||
|
@ -418,9 +418,9 @@ describe 'POST /db/article/:handle/new-version', ->
|
|||
|
||||
|
||||
it 'works for artisans', utils.wrap (done) ->
|
||||
yield utils.logoutAsync()
|
||||
artisan = yield utils.initArtisanAsync()
|
||||
yield utils.loginUserAsync(artisan)
|
||||
yield utils.logout()
|
||||
artisan = yield utils.initArtisan()
|
||||
yield utils.loginUser(artisan)
|
||||
yield postNewVersion({ name: 'Article name', body: 'New body' })
|
||||
articles = yield Article.find()
|
||||
expect(articles.length).toBe(2)
|
||||
|
@ -428,9 +428,9 @@ describe 'POST /db/article/:handle/new-version', ->
|
|||
|
||||
|
||||
it 'works for normal users submitting translations', utils.wrap (done) ->
|
||||
yield utils.logoutAsync()
|
||||
user = yield utils.initUserAsync()
|
||||
yield utils.loginUserAsync(user)
|
||||
yield utils.logout()
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
yield postNewVersion({ name: 'Article name', body: 'Article body', i18n: { fr: { name: 'Le Article' }}}, 201)
|
||||
articles = yield Article.find()
|
||||
expect(articles.length).toBe(2)
|
||||
|
@ -438,9 +438,9 @@ describe 'POST /db/article/:handle/new-version', ->
|
|||
|
||||
|
||||
it 'does not work for normal users', utils.wrap (done) ->
|
||||
yield utils.logoutAsync()
|
||||
user = yield utils.initUserAsync()
|
||||
yield utils.loginUserAsync(user)
|
||||
yield utils.logout()
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
yield postNewVersion({ name: 'Article name', body: 'New body' }, 403)
|
||||
articles = yield Article.find()
|
||||
expect(articles.length).toBe(1)
|
||||
|
@ -448,7 +448,7 @@ describe 'POST /db/article/:handle/new-version', ->
|
|||
|
||||
|
||||
it 'does not work for anonymous users', utils.wrap (done) ->
|
||||
yield utils.logoutAsync()
|
||||
yield utils.logout()
|
||||
yield postNewVersion({ name: 'Article name', body: 'New body' }, 401)
|
||||
articles = yield Article.find()
|
||||
expect(articles.length).toBe(1)
|
||||
|
@ -490,9 +490,9 @@ describe 'version fetching endpoints', ->
|
|||
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
@admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(@admin)
|
||||
yield utils.clearModels([Article])
|
||||
@admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(@admin)
|
||||
[res, body] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
|
||||
expect(res.statusCode).toBe(201)
|
||||
articleOriginal = body._id
|
||||
|
@ -548,10 +548,10 @@ describe 'version fetching endpoints', ->
|
|||
describe 'GET /db/article/:handle/files', ->
|
||||
|
||||
it 'returns an array of file metadata for the given original article', utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
yield utils.clearModels([Article])
|
||||
articleData = { name: 'Article', body: 'Article' }
|
||||
admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(admin)
|
||||
admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(admin)
|
||||
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
|
||||
expect(res.statusCode).toBe(201)
|
||||
[res, body] = yield request.postAsync(getURL('/file'), { json: {
|
||||
|
@ -572,12 +572,12 @@ describe 'GET and POST /db/article/:handle/names', ->
|
|||
articleData2 = { name: 'Article 2', body: 'Article 2 body' }
|
||||
|
||||
it 'returns an object mapping ids to names', utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(admin)
|
||||
yield utils.clearModels([Article])
|
||||
admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(admin)
|
||||
[res, article1] = yield request.postAsync(getURL('/db/article'), { json: articleData1 })
|
||||
[res, article2] = yield request.postAsync(getURL('/db/article'), { json: articleData2 })
|
||||
yield utils.logoutAsync()
|
||||
yield utils.logout()
|
||||
[res, body] = yield request.getAsync { uri: getURL('/db/article/names?ids='+[article1._id, article2._id].join(',')), json: true }
|
||||
expect(body.length).toBe(2)
|
||||
expect(body[0].name).toBe('Article 1')
|
||||
|
@ -590,10 +590,10 @@ describe 'GET and POST /db/article/:handle/names', ->
|
|||
describe 'GET /db/article/:handle/patches', ->
|
||||
|
||||
it 'returns pending patches for the given original article', utils.wrap (done) ->
|
||||
yield utils.clearModelsAsync([Article])
|
||||
yield utils.clearModels([Article])
|
||||
articleData = { name: 'Article', body: 'Article' }
|
||||
admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(admin)
|
||||
admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(admin)
|
||||
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
|
||||
expect(res.statusCode).toBe(201)
|
||||
[res, patch] = yield request.postAsync { uri: getURL('/db/patch'), json: {
|
||||
|
@ -620,17 +620,17 @@ describe 'POST /db/article/:handle/watchers', ->
|
|||
|
||||
it 'adds self to the list of watchers, and is idempotent', utils.wrap (done) ->
|
||||
# create article
|
||||
yield utils.clearModelsAsync([Article])
|
||||
yield utils.clearModels([Article])
|
||||
articleData = { name: 'Article', body: 'Article' }
|
||||
admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(admin)
|
||||
admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(admin)
|
||||
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
|
||||
expect(res.statusCode).toBe(201)
|
||||
|
||||
# add new user as watcher
|
||||
yield utils.logoutAsync()
|
||||
user = yield utils.initUserAsync()
|
||||
yield utils.loginUserAsync(user)
|
||||
yield utils.logout()
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
[res, article] = yield request.postAsync { uri: getURL("/db/article/#{article._id}/watchers"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(_.contains(article.watchers, user.id)).toBe(true)
|
||||
|
@ -649,17 +649,17 @@ describe 'DELETE /db/article/:handle/watchers', ->
|
|||
|
||||
it 'removes self from the list of watchers, and is idempotent', utils.wrap (done) ->
|
||||
# create article
|
||||
yield utils.clearModelsAsync([Article])
|
||||
yield utils.clearModels([Article])
|
||||
articleData = { name: 'Article', body: 'Article' }
|
||||
admin = yield utils.initAdminAsync({})
|
||||
yield utils.loginUserAsync(admin)
|
||||
admin = yield utils.initAdmin({})
|
||||
yield utils.loginUser(admin)
|
||||
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
|
||||
expect(res.statusCode).toBe(201)
|
||||
|
||||
# add new user as watcher
|
||||
yield utils.logoutAsync()
|
||||
user = yield utils.initUserAsync()
|
||||
yield utils.loginUserAsync(user)
|
||||
yield utils.logout()
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
[res, article] = yield request.postAsync { uri: getURL("/db/article/#{article._id}/watchers"), json: true }
|
||||
expect(_.contains(article.watchers, user.id)).toBe(true)
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
async = require 'async'
|
||||
utils = require '../../server/lib/utils'
|
||||
co = require 'co'
|
||||
Promise = require 'bluebird'
|
||||
|
||||
module.exports = mw =
|
||||
getURL: (path) -> 'http://localhost:3001' + path
|
||||
|
||||
clearModels: (models, done) ->
|
||||
clearModels: Promise.promisify (models, done) ->
|
||||
funcs = []
|
||||
for model in models
|
||||
wrapped = (m) ->
|
||||
|
@ -25,11 +26,11 @@ module.exports = mw =
|
|||
password: 'password'
|
||||
permissions: options.permissions
|
||||
}
|
||||
new User(doc).save (err, user) ->
|
||||
expect(err).toBe(null)
|
||||
done(err, user)
|
||||
user = new User(doc)
|
||||
promise = user.save()
|
||||
return promise
|
||||
|
||||
loginUser: (user, done) ->
|
||||
loginUser: Promise.promisify (user, done) ->
|
||||
form = {
|
||||
username: user.get('email')
|
||||
password: 'password'
|
||||
|
@ -39,21 +40,21 @@ module.exports = mw =
|
|||
expect(res.statusCode).toBe(200)
|
||||
done(err, user)
|
||||
|
||||
initAdmin: (options, done) ->
|
||||
initAdmin: (options) ->
|
||||
if _.isFunction(options)
|
||||
done = options
|
||||
options = {}
|
||||
options = _.extend({permissions: ['admin']}, options)
|
||||
return @initUser(options, done)
|
||||
return @initUser(options)
|
||||
|
||||
initArtisan: (options, done) ->
|
||||
initArtisan: (options) ->
|
||||
if _.isFunction(options)
|
||||
done = options
|
||||
options = {}
|
||||
options = _.extend({permissions: ['artisan']}, options)
|
||||
return @initUser(options, done)
|
||||
return @initUser(options)
|
||||
|
||||
logout: (done) ->
|
||||
logout: Promise.promisify (done) ->
|
||||
request.post mw.getURL('/auth/logout'), done
|
||||
|
||||
wrap: (gen) ->
|
||||
|
@ -61,7 +62,3 @@ module.exports = mw =
|
|||
return (done) ->
|
||||
fn.apply(@, [done]).catch (err) -> done.fail(err)
|
||||
|
||||
|
||||
|
||||
Promise = require 'bluebird'
|
||||
Promise.promisifyAll(module.exports)
|
Loading…
Add table
Add a link
Reference in a new issue