Просмотр исходного кода

Add POST end point for updating doc lines

James Allen 12 лет назад
Родитель
Сommit
2e8582874f

+ 1 - 0
services/docstore/.gitignore

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

+ 9 - 7
services/docstore/app.coffee

@@ -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"
 HttpController = require "./app/js/HttpController"
-Errors = require "./app/js/Errors"
+
+logger.initialize("docstore")
 
 
 app = express()
 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)->
 app.get '/status', (req, res)->
 	res.send('docstore is alive')
 	res.send('docstore is alive')

+ 3 - 3
services/docstore/app/coffee/DocManager.coffee

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

+ 10 - 0
services/docstore/app/coffee/Errors.coffee

@@ -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
+

+ 21 - 2
services/docstore/app/coffee/HttpController.coffee

@@ -4,11 +4,30 @@ logger = require "logger-sharelatex"
 module.exports = HttpController =
 module.exports = HttpController =
 	getDoc: (req, res, next = (error) ->) ->
 	getDoc: (req, res, next = (error) ->) ->
 		project_id = req.params.project_id
 		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"
 		logger.log project_id: project_id, doc_id: doc_id, "getting doc"
 		DocManager.getDoc project_id, doc_id, (error, doc) ->
 		DocManager.getDoc project_id, doc_id, (error, doc) ->
 			return next(error) if error?
 			return next(error) if error?
 			if !doc?
 			if !doc?
 				res.send 404
 				res.send 404
 			else
 			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
+			}

+ 4 - 2
services/docstore/package.json

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

+ 30 - 0
services/docstore/test/acceptance/coffee/GettingDocsTests.coffee

@@ -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()
+

+ 89 - 0
services/docstore/test/acceptance/coffee/UpdatingDocsTests.coffee

@@ -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()
+

+ 37 - 0
services/docstore/test/acceptance/coffee/helpers/DocstoreClient.coffee

@@ -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
+		
+		
+		
+	

+ 2 - 2
services/docstore/test/unit/coffee/DocManagerTests.coffee

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

+ 59 - 24
services/docstore/test/unit/coffee/HttpControllerTests.coffee

@@ -9,8 +9,8 @@ describe "HttpController", ->
 	beforeEach ->
 	beforeEach ->
 		@HttpController = SandboxedModule.require modulePath, requires:
 		@HttpController = SandboxedModule.require modulePath, requires:
 			"./DocManager": @DocManager = {}
 			"./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 = {}
 		@req = {}
 		@next = sinon.stub()
 		@next = sinon.stub()
 		@project_id = "mock-project-id"
 		@project_id = "mock-project-id"
@@ -21,33 +21,68 @@ describe "HttpController", ->
 		}
 		}
 
 
 	describe "getDoc", ->
 	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 ->
 			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)
+				@req.body =
+					lines: @lines = ["hello", "world"]
+				@DocManager.updateDoc = sinon.stub().callsArgWith(3, null, true)
+				@HttpController.updateDoc @req, @res, @next
+
+			it "should update the document", ->
+				@DocManager.updateDoc
+					.calledWith(@project_id, @doc_id, @lines)
 					.should.equal true
 					.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
 					.should.equal true
 
 
-		describe "when the doc does not exist", ->
+		describe "when the doc lines exist and were not updated", ->
 			beforeEach ->
 			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", ->
+			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
 				@res.send
-					.calledWith(404)
-					.should.equal true
+					.calledWith(400)
+					.should.equal true