diff --git a/services/docstore/app.coffee b/services/docstore/app.coffee index 8b89bb97ec..f8024ec188 100644 --- a/services/docstore/app.coffee +++ b/services/docstore/app.coffee @@ -9,6 +9,7 @@ logger.initialize("docstore") app = express() +app.get '/project/:project_id/doc', HttpController.getAllDocs app.get '/project/:project_id/doc/:doc_id', HttpController.getDoc app.post '/project/:project_id/doc/:doc_id', bodyParser.json(), HttpController.updateDoc app.del '/project/:project_id/doc/:doc_id', HttpController.deleteDoc diff --git a/services/docstore/app/coffee/DocManager.coffee b/services/docstore/app/coffee/DocManager.coffee index 0d730ad7de..1f8bf64514 100644 --- a/services/docstore/app/coffee/DocManager.coffee +++ b/services/docstore/app/coffee/DocManager.coffee @@ -13,6 +13,14 @@ module.exports = DocManager = return callback new Errors.NotFoundError("No such doc: #{project_id}") if !doc? return callback null, doc, mongoPath + getAllDocs: (project_id, callback = (error, docs) ->) -> + MongoManager.findProject project_id, (error, project) -> + return callback(error) if error? + return callback new Errors.NotFoundError("No such project: #{project_id}") if !project? + DocManager.findAllDocsInProject project, (error, docs) -> + return callback(error) if error? + return callback null, docs + updateDoc: (project_id, doc_id, lines, callback = (error, modified) ->) -> DocManager.getDoc project_id, doc_id, (error, doc, mongoPath) -> return callback(error) if error? @@ -39,6 +47,9 @@ module.exports = DocManager = return callback(error) if error? callback() + findAllDocsInProject: (project, callback = (error, docs) ->) -> + callback null, @_findAllDocsInFolder project.rootFolder[0] + findDocInProject: (project, doc_id, callback = (error, doc, mongoPath) ->) -> result = @_findDocInFolder project.rootFolder[0], doc_id, "rootFolder.0" if result? @@ -58,4 +69,11 @@ module.exports = DocManager = result = @_findDocInFolder childFolder, doc_id, "#{currentPath}.folders.#{i}" return result if result? - return null \ No newline at end of file + return null + + _findAllDocsInFolder: (folder) -> + docs = folder.docs or [] + for childFolder in folder.folders or [] + docs = docs.concat @_findAllDocsInFolder childFolder + return docs + diff --git a/services/docstore/app/coffee/HttpController.coffee b/services/docstore/app/coffee/HttpController.coffee index 314d1490b1..781376b5a5 100644 --- a/services/docstore/app/coffee/HttpController.coffee +++ b/services/docstore/app/coffee/HttpController.coffee @@ -11,9 +11,14 @@ module.exports = HttpController = if !doc? res.send 404 else - res.json { - lines: doc.lines - } + res.json HttpController._buildDocView(doc) + + getAllDocs: (req, res, next = (error) ->) -> + project_id = req.params.project_id + logger.log project_id: project_id, "getting all docs" + DocManager.getAllDocs project_id, (error, docs = []) -> + return next(error) if error? + res.json docs.map(HttpController._buildDocView) updateDoc: (req, res, next = (error) ->) -> project_id = req.params.project_id @@ -38,4 +43,12 @@ module.exports = HttpController = logger.log project_id: project_id, doc_id: doc_id, "deleting doc" DocManager.deleteDoc project_id, doc_id, (error) -> return next(error) if error? - res.send 204 \ No newline at end of file + res.send 204 + + _buildDocView: (doc) -> + return { + _id: doc._id.toString() + lines: doc.lines + rev: doc.rev + version: doc.version + } \ No newline at end of file diff --git a/services/docstore/package.json b/services/docstore/package.json index d3d13b2278..029f13cebc 100644 --- a/services/docstore/package.json +++ b/services/docstore/package.json @@ -9,7 +9,8 @@ "mongojs": "0.9.11", "express": "~4.1.1", "underscore": "~1.6.0", - "body-parser": "~1.0.2" + "body-parser": "~1.0.2", + "async": "~0.8.0" }, "devDependencies": { "grunt-execute": "~0.2.1", diff --git a/services/docstore/test/acceptance/coffee/DeletingDocsTests.coffee b/services/docstore/test/acceptance/coffee/DeletingDocsTests.coffee index a82906be89..a1dbe937da 100644 --- a/services/docstore/test/acceptance/coffee/DeletingDocsTests.coffee +++ b/services/docstore/test/acceptance/coffee/DeletingDocsTests.coffee @@ -5,12 +5,16 @@ chai.should() DocstoreClient = require "./helpers/DocstoreClient" -describe "Applying updates to a doc", -> +describe "Deleting a doc", -> beforeEach (done) -> @project_id = ObjectId() + @doc_id = ObjectId() @lines = ["original", "lines"] - DocstoreClient.createDoc @project_id, @lines, (error, @doc_id) => - done() + DocstoreClient.createProject @project_id, (error) => + throw error if error? + DocstoreClient.createDoc @project_id, @doc_id, @lines, (error) => + throw error if error? + done() afterEach (done) -> DocstoreClient.deleteProject @project_id, done diff --git a/services/docstore/test/acceptance/coffee/GettingAllDocsTests.coffee b/services/docstore/test/acceptance/coffee/GettingAllDocsTests.coffee new file mode 100644 index 0000000000..568e8f9554 --- /dev/null +++ b/services/docstore/test/acceptance/coffee/GettingAllDocsTests.coffee @@ -0,0 +1,45 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +{ObjectId} = require "mongojs" +async = require "async" + +DocstoreClient = require "./helpers/DocstoreClient" + +describe "Getting all docs", -> + beforeEach (done) -> + @project_id = ObjectId() + @docs = [{ + _id: ObjectId() + lines: ["one"] + version: 1 + rev: 2 + }, { + _id: ObjectId() + lines: ["two"] + version: 3 + rev: 4 + }, { + _id: ObjectId() + lines: ["three"] + version: 5 + rev: 6 + }] + DocstoreClient.createProject @project_id, (error) => + throw error if error? + jobs = for doc in @docs + do (doc) => + (callback) => DocstoreClient.createDoc @project_id, doc._id, doc.lines, callback + async.series jobs, done + + afterEach (done) -> + DocstoreClient.deleteProject @project_id, done + + it "should return all the docs", (done) -> + DocstoreClient.getAllDocs @project_id, (error, res, docs) => + throw error if error? + docs.length.should.equal @docs.length + for doc, i in docs + doc.lines.should.deep.equal @docs[i].lines + done() + diff --git a/services/docstore/test/acceptance/coffee/GettingDocsTests.coffee b/services/docstore/test/acceptance/coffee/GettingDocsTests.coffee index 398f8b3690..248286b8e4 100644 --- a/services/docstore/test/acceptance/coffee/GettingDocsTests.coffee +++ b/services/docstore/test/acceptance/coffee/GettingDocsTests.coffee @@ -5,12 +5,16 @@ chai.should() DocstoreClient = require "./helpers/DocstoreClient" -describe "Applying updates to a doc", -> +describe "Getting a doc", -> beforeEach (done) -> @project_id = ObjectId() + @doc_id = ObjectId() @lines = ["original", "lines"] - DocstoreClient.createDoc @project_id, @lines, (error, @doc_id) => - done() + DocstoreClient.createProject @project_id, (error) => + throw error if error? + DocstoreClient.createDoc @project_id, @doc_id, @lines, (error) => + throw error if error? + done() afterEach (done) -> DocstoreClient.deleteProject @project_id, done diff --git a/services/docstore/test/acceptance/coffee/UpdatingDocsTests.coffee b/services/docstore/test/acceptance/coffee/UpdatingDocsTests.coffee index 107967ebd3..740aadb446 100644 --- a/services/docstore/test/acceptance/coffee/UpdatingDocsTests.coffee +++ b/services/docstore/test/acceptance/coffee/UpdatingDocsTests.coffee @@ -8,10 +8,14 @@ DocstoreClient = require "./helpers/DocstoreClient" describe "Applying updates to a doc", -> beforeEach (done) -> @project_id = ObjectId() + @doc_id = ObjectId() @originalLines = ["original", "lines"] @newLines = ["new", "lines"] - DocstoreClient.createDoc @project_id, @originalLines, (error, @doc_id) => - done() + DocstoreClient.createProject @project_id, (error) => + throw error if error? + DocstoreClient.createDoc @project_id, @doc_id, @lines, (error) => + throw error if error? + done() afterEach (done) -> DocstoreClient.deleteProject @project_id, done diff --git a/services/docstore/test/acceptance/coffee/helpers/DocstoreClient.coffee b/services/docstore/test/acceptance/coffee/helpers/DocstoreClient.coffee index 6aec513246..7640501537 100644 --- a/services/docstore/test/acceptance/coffee/helpers/DocstoreClient.coffee +++ b/services/docstore/test/acceptance/coffee/helpers/DocstoreClient.coffee @@ -2,29 +2,39 @@ 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() + createProject: (project_id, callback = (error) ->) -> db.projects.insert { _id: project_id - rootFolder: [{ - docs: [{ - _id: doc_id, + rootFolder: [{ docs: [] }] + }, callback + + createDoc: (project_id, doc_id, lines, callback = (error) ->) -> + db.projects.update { + _id: project_id + }, { + $push: { + "rootFolder.0.docs": { + _id: doc_id lines: lines - }] - }] - }, (error) -> - return callback(error) if error? - callback null, doc_id + } + } + }, callback deleteProject: (project_id, callback = (error, res, body) ->) -> db.projects.remove _id: project_id, callback - getDoc: (project_id, doc_id, callback = (error, doc) ->) -> + getDoc: (project_id, doc_id, callback = (error, res, body) ->) -> request.get { url: "http://localhost:3016/project/#{project_id}/doc/#{doc_id}" json: true }, callback + getAllDocs: (project_id, callback = (error, res, body) ->) -> + request.get { + url: "http://localhost:3016/project/#{project_id}/doc" + 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}" diff --git a/services/docstore/test/unit/coffee/DocManagerTests.coffee b/services/docstore/test/unit/coffee/DocManagerTests.coffee index 0ab746e2d2..0b2b70c38f 100644 --- a/services/docstore/test/unit/coffee/DocManagerTests.coffee +++ b/services/docstore/test/unit/coffee/DocManagerTests.coffee @@ -41,7 +41,7 @@ describe "DocManager", -> describe "when the project does not exist", -> beforeEach -> - @MongoManager.findProject = sinon.stub().callsArgWith(1, null, @null) + @MongoManager.findProject = sinon.stub().callsArgWith(1, null, null) @DocManager.findDocInProject = sinon.stub() @DocManager.getDoc @project_id, @doc_id, @callback @@ -70,6 +70,43 @@ describe "DocManager", -> .calledWith(new Errors.NotFoundError("No such doc: #{@doc_id}")) .should.equal true + describe "getAllDocs", -> + describe "when the project exists", -> + beforeEach -> + @project = { name: "mock-project" } + @docs = [{ _id: @doc_id, lines: ["mock-lines"] }] + @mongoPath = "mock.mongo.path" + @MongoManager.findProject = sinon.stub().callsArgWith(1, null, @project) + @DocManager.findAllDocsInProject = sinon.stub().callsArgWith(1, null, @docs) + @DocManager.getAllDocs @project_id, @callback + + it "should get the project from the database", -> + @MongoManager.findProject + .calledWith(@project_id) + .should.equal true + + it "should find all the docs in the project", -> + @DocManager.findAllDocsInProject + .calledWith(@project) + .should.equal true + + it "should return the docs", -> + @callback.calledWith(null, @docs).should.equal true + + describe "when the project does not exist", -> + beforeEach -> + @MongoManager.findProject = sinon.stub().callsArgWith(1, null, null) + @DocManager.findAllDocsInProject = sinon.stub() + @DocManager.getDoc @project_id, @doc_id, @callback + + it "should not try to find the doc in the project", -> + @DocManager.findAllDocsInProject.called.should.equal false + + it "should return a NotFoundError", -> + @callback + .calledWith(new Errors.NotFoundError("No such project: #{@project_id}")) + .should.equal true + describe "deleteDoc", -> describe "when the doc exists", -> beforeEach -> @@ -232,3 +269,25 @@ describe "DocManager", -> expect(doc).to.be.null expect(mongoPath).to.be.null done() + + describe "findAllDocsInProject", -> + it "should return all the docs", (done) -> + @DocManager.findAllDocsInProject { + rootFolder: [{ + docs: [ + @doc1 = { _id: ObjectId() } + ], + folders: [{ + docs: [ + @doc2 = { _id: ObjectId() } + ] + }, { + docs: [ + @doc3 = { _id: ObjectId() } + @doc4 = { _id: ObjectId() } + ] + }] + }] + }, (error, docs) => + expect(docs).to.deep.equal [@doc1, @doc2, @doc3, @doc4] + done() diff --git a/services/docstore/test/unit/coffee/HttpControllerTests.coffee b/services/docstore/test/unit/coffee/HttpControllerTests.coffee index 576fd66122..ba8a6e7535 100644 --- a/services/docstore/test/unit/coffee/HttpControllerTests.coffee +++ b/services/docstore/test/unit/coffee/HttpControllerTests.coffee @@ -4,6 +4,7 @@ chai = require('chai') chai.should() expect = chai.expect modulePath = require('path').join __dirname, '../../../app/js/HttpController' +ObjectId = require("mongojs").ObjectId describe "HttpController", -> beforeEach -> @@ -18,6 +19,8 @@ describe "HttpController", -> @doc = { _id: @doc_id lines: ["mock", "lines"] + version: 42 + rev: 5 } describe "getDoc", -> @@ -35,7 +38,50 @@ describe "HttpController", -> it "should return the doc as JSON", -> @res.json - .calledWith(lines: @doc.lines) + .calledWith({ + _id: @doc_id + lines: @doc.lines + rev: @doc.rev + version: @doc.version + }) + .should.equal true + + describe "getAllDocs", -> + beforeEach -> + @req.params = + project_id: @project_id + @docs = [{ + _id: ObjectId() + lines: ["mock", "lines", "one"] + version: 1 + rev: 2 + }, { + _id: ObjectId() + lines: ["mock", "lines", "two"] + version: 3 + rev: 4 + }] + @DocManager.getAllDocs = sinon.stub().callsArgWith(1, null, @docs) + @HttpController.getAllDocs @req, @res, @next + + it "should get all the docs", -> + @DocManager.getAllDocs + .calledWith(@project_id) + .should.equal true + + it "should return the doc as JSON", -> + @res.json + .calledWith([{ + _id: @docs[0]._id.toString() + lines: @docs[0].lines + rev: @docs[0].rev + version: @docs[0].version + }, { + _id: @docs[1]._id.toString() + lines: @docs[1].lines + rev: @docs[1].rev + version: @docs[1].version + }]) .should.equal true describe "updateDoc", ->