Add POST end point for updating doc lines

This commit is contained in:
James Allen 2014-04-29 11:49:09 +01:00
parent 94b25055fc
commit 2e8582874f
11 changed files with 265 additions and 40 deletions

View file

@ -1,5 +1,6 @@
node_modules
app/js/
test/unit/js
test/acceptance/js
app.js
forever

View file

@ -1,14 +1,16 @@
Settings = require('settings-sharelatex')
logger = require('logger-sharelatex')
logger.initialize("docstore")
express = require('express')
Settings = require "settings-sharelatex"
logger = require "logger-sharelatex"
express = require "express"
bodyParser = require "body-parser"
Errors = require "./app/js/Errors"
HttpController = require "./app/js/HttpController"
Errors = require "./app/js/Errors"
logger.initialize("docstore")
app = express()
app.get '/project/:project_id/doc/:doc_id', HttpController.getDoc
app.get '/project/:project_id/doc/:doc_id', HttpController.getDoc
app.post '/project/:project_id/doc/:doc_id', bodyParser.json(), HttpController.updateDoc
app.get '/status', (req, res)->
res.send('docstore is alive')

View file

@ -13,7 +13,7 @@ module.exports = DocManager =
return callback new Errors.NotFoundError("No such doc: #{project_id}") if !doc?
return callback null, doc, mongoPath
updateDoc: (project_id, doc_id, lines, callback = (error) ->) ->
updateDoc: (project_id, doc_id, lines, callback = (error, modified) ->) ->
DocManager.getDoc project_id, doc_id, (error, doc, mongoPath) ->
return callback(error) if error?
return callback new Errors.NotFoundError("No such project/doc: #{project_id}/#{doc_id}") if !doc?
@ -22,14 +22,14 @@ module.exports = DocManager =
logger.log {
project_id: project_id, doc_id: doc_id, rev: doc.rev
}, "doc lines have not changed"
return callback()
return callback null, false
else
logger.log {
project_id: project_id, doc_id: doc_id, oldDocLines: doc.lines, newDocLines: lines, rev: doc.rev
}, "updating doc lines"
MongoManager.updateDoc project_id, mongoPath, lines, (error) ->
return callback(error) if error?
callback()
callback null, true
findDocInProject: (project, doc_id, callback = (error, doc, mongoPath) ->) ->
result = @_findDocInFolder project.rootFolder[0], doc_id, "rootFolder.0"

View file

@ -0,0 +1,10 @@
NotFoundError = (message) ->
error = new Error(message)
error.name = "NotFoundError"
error.__proto__ = NotFoundError.prototype
return error
NotFoundError.prototype.__proto__ = Error.prototype
module.exports = Errors =
NotFoundError: NotFoundError

View file

@ -4,11 +4,30 @@ logger = require "logger-sharelatex"
module.exports = HttpController =
getDoc: (req, res, next = (error) ->) ->
project_id = req.params.project_id
doc_id = req.params.doc_id
doc_id = req.params.doc_id
logger.log project_id: project_id, doc_id: doc_id, "getting doc"
DocManager.getDoc project_id, doc_id, (error, doc) ->
return next(error) if error?
if !doc?
res.send 404
else
res.send JSON.stringify({ lines: doc.lines })
res.json {
lines: doc.lines
}
updateDoc: (req, res, next = (error) ->) ->
project_id = req.params.project_id
doc_id = req.params.doc_id
lines = req.body?.lines
if !lines? or lines not instanceof Array
logger.error project_id: project_id, doc_id: doc_id, "no doc lines provided"
res.send 400 # Bad Request
return
logger.log project_id: project_id, doc_id: doc_id, "updating doc"
DocManager.updateDoc project_id, doc_id, lines, (error, modified) ->
return next(error) if error?
res.json {
modified: modified
}

View file

@ -8,7 +8,8 @@
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master",
"mongojs": "0.9.11",
"express": "~4.1.1",
"underscore": "~1.6.0"
"underscore": "~1.6.0",
"body-parser": "~1.0.2"
},
"devDependencies": {
"grunt-execute": "~0.2.1",
@ -22,6 +23,7 @@
"sinon": "~1.5.2",
"sandboxed-module": "~0.3.0",
"chai": "~1.9.1",
"grunt-forever": "~0.4.4"
"grunt-forever": "~0.4.4",
"request": "~2.34.0"
}
}

View file

@ -0,0 +1,30 @@
sinon = require "sinon"
chai = require("chai")
chai.should()
{ObjectId} = require "mongojs"
DocstoreClient = require "./helpers/DocstoreClient"
describe "Applying updates to a doc", ->
beforeEach (done) ->
@project_id = ObjectId()
@lines = ["original", "lines"]
DocstoreClient.createDoc @project_id, @lines, (error, @doc_id) =>
done()
afterEach (done) ->
DocstoreClient.deleteProject @project_id, done
describe "when the doc exists", ->
it "should get the doc lines", (done) ->
DocstoreClient.getDoc @project_id, @doc_id, (error, res, doc) =>
doc.lines.should.deep.equal @lines
done()
describe "when the doc does not exist", ->
it "should return a 404", (done) ->
missing_doc_id = ObjectId()
DocstoreClient.getDoc @project_id, missing_doc_id, (error, res, doc) ->
res.statusCode.should.equal 404
done()

View file

@ -0,0 +1,89 @@
sinon = require "sinon"
chai = require("chai")
chai.should()
{ObjectId} = require "mongojs"
DocstoreClient = require "./helpers/DocstoreClient"
describe "Applying updates to a doc", ->
beforeEach (done) ->
@project_id = ObjectId()
@originalLines = ["original", "lines"]
@newLines = ["new", "lines"]
DocstoreClient.createDoc @project_id, @originalLines, (error, @doc_id) =>
done()
afterEach (done) ->
DocstoreClient.deleteProject @project_id, done
describe "when the content has changed", ->
beforeEach (done) ->
DocstoreClient.updateDoc @project_id, @doc_id, @newLines, (error, res, @body) =>
done()
it "should return modified = true", ->
@body.modified.should.equal true
it "should update the doc in the API", (done) ->
DocstoreClient.getDoc @project_id, @doc_id, (error, res, doc) =>
doc.lines.should.deep.equal @newLines
done()
describe "when the content has not been updated", ->
beforeEach (done) ->
DocstoreClient.updateDoc @project_id, @doc_id, @originalLines, (error, res, @body) =>
done()
it "should return modified = false", ->
@body.modified.should.equal false
it "should not update the doc in the API", (done) ->
DocstoreClient.getDoc @project_id, @doc_id, (error, res, doc) =>
doc.lines.should.deep.equal @originalLines
done()
describe "when the doc does not exist", ->
beforeEach (done) ->
missing_doc_id = ObjectId()
DocstoreClient.updateDoc @project_id, missing_doc_id, @originalLines, (error, @res, @body) =>
done()
it "should return a 404", ->
@res.statusCode.should.equal 404
describe "when the project does not exist", ->
beforeEach (done) ->
missing_project_id = ObjectId()
DocstoreClient.updateDoc missing_project_id, @doc_id, @originalLines, (error, @res, @body) =>
done()
it "should return a 404", ->
@res.statusCode.should.equal 404
describe "when malformed doc lines are provided", ->
describe "when the lines are not an array", ->
beforeEach (done) ->
DocstoreClient.updateDoc @project_id, @doc_id, { foo: "bar" }, (error, @res, @body) =>
done()
it "should return 400", ->
@res.statusCode.should.equal 400
it "should not update the doc in the API", (done) ->
DocstoreClient.getDoc @project_id, @doc_id, (error, res, doc) =>
doc.lines.should.deep.equal @originalLines
done()
describe "when the lines are not present", ->
beforeEach (done) ->
DocstoreClient.updateDoc @project_id, @doc_id, null, (error, @res, @body) =>
done()
it "should return 400", ->
@res.statusCode.should.equal 400
it "should not update the doc in the API", (done) ->
DocstoreClient.getDoc @project_id, @doc_id, (error, res, doc) =>
doc.lines.should.deep.equal @originalLines
done()

View file

@ -0,0 +1,37 @@
request = require("request").defaults(jar: false)
{db, ObjectId} = require("../../../../app/js/mongojs")
module.exports = DocstoreClient =
createDoc: (project_id, lines, callback = (error, doc_id) ->) ->
doc_id = ObjectId()
db.projects.insert {
_id: project_id
rootFolder: [{
docs: [{
_id: doc_id,
lines: lines
}]
}]
}, (error) ->
return callback(error) if error?
callback null, doc_id
deleteProject: (project_id, callback = (error, res, body) ->) ->
db.projects.remove _id: project_id, callback
getDoc: (project_id, doc_id, callback = (error, doc) ->) ->
request.get {
url: "http://localhost:3016/project/#{project_id}/doc/#{doc_id}"
json: true
}, callback
updateDoc: (project_id, doc_id, lines, callback = (error, res, body) ->) ->
request.post {
url: "http://localhost:3016/project/#{project_id}/doc/#{doc_id}"
json:
lines: lines
}, callback

View file

@ -107,7 +107,7 @@ describe "DocManager", ->
.should.equal true
it "should return the callback", ->
@callback.called.should.equal true
@callback.calledWith(null, true).should.equal true
describe "when the doc lines have not changed", ->
beforeEach ->
@ -118,7 +118,7 @@ describe "DocManager", ->
@MongoManager.updateDoc.called.should.equal false
it "should return the callback", ->
@callback.called.should.equal true
@callback.calledWith(null, false).should.equal true
describe "when the doc does not exist", ->
beforeEach ->

View file

@ -9,8 +9,8 @@ describe "HttpController", ->
beforeEach ->
@HttpController = SandboxedModule.require modulePath, requires:
"./DocManager": @DocManager = {}
"logger-sharelatex": @logger = { log: sinon.stub() }
@res = { send: sinon.stub() }
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
@res = { send: sinon.stub(), json: sinon.stub() }
@req = {}
@next = sinon.stub()
@project_id = "mock-project-id"
@ -21,33 +21,68 @@ describe "HttpController", ->
}
describe "getDoc", ->
describe "when the doc exists", ->
beforeEach ->
@req.params =
project_id: @project_id
doc_id: @doc_id
@DocManager.getDoc = sinon.stub().callsArgWith(2, null, @doc)
@HttpController.getDoc @req, @res, @next
it "should get the document", ->
@DocManager.getDoc
.calledWith(@project_id, @doc_id)
.should.equal true
it "should return the doc as JSON", ->
@res.json
.calledWith(lines: @doc.lines)
.should.equal true
describe "updateDoc", ->
beforeEach ->
@req.params =
project_id: @project_id
doc_id: @doc_id
describe "when the doc lines exist and were updated", ->
beforeEach ->
@req.params =
project_id: @project_id
doc_id: @doc_id
@DocManager.getDoc = sinon.stub().callsArgWith(2, null, @doc)
@HttpController.getDoc @req, @res, @next
@req.body =
lines: @lines = ["hello", "world"]
@DocManager.updateDoc = sinon.stub().callsArgWith(3, null, true)
@HttpController.updateDoc @req, @res, @next
it "should get the document", ->
@DocManager.getDoc
.calledWith(@project_id, @doc_id)
it "should update the document", ->
@DocManager.updateDoc
.calledWith(@project_id, @doc_id, @lines)
.should.equal true
it "should return the doc as JSON", ->
@res.send
.calledWith(JSON.stringify(lines: @doc.lines))
it "should return a modified status", ->
@res.json
.calledWith(modified: true)
.should.equal true
describe "when the doc does not exist", ->
describe "when the doc lines exist and were not updated", ->
beforeEach ->
@req.params =
project_id: @project_id
doc_id: @doc_id
@DocManager.getDoc = sinon.stub().callsArgWith(2, null, null)
@HttpController.getDoc @req, @res, @next
@req.body =
lines: @lines = ["hello", "world"]
@DocManager.updateDoc = sinon.stub().callsArgWith(3, null, false)
@HttpController.updateDoc @req, @res, @next
it "should return a 404", ->
@res.send
.calledWith(404)
it "should return a modified status", ->
@res.json
.calledWith(modified: false)
.should.equal true
describe "when the doc lines are not provided", ->
beforeEach ->
@req.body = {}
@DocManager.updateDoc = sinon.stub().callsArgWith(3, null, false)
@HttpController.updateDoc @req, @res, @next
it "should not update the document", ->
@DocManager.updateDoc.called.should.equal false
it "should return a 400 (bad request) response", ->
@res.send
.calledWith(400)
.should.equal true