mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 13:13:45 -05:00
Create a GET endpoint for getting doc lines
This commit is contained in:
commit
6eb328e788
12 changed files with 401 additions and 0 deletions
4
services/docstore/.gitignore
vendored
Normal file
4
services/docstore/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules
|
||||||
|
app/js/
|
||||||
|
test/unit/js
|
||||||
|
app.js
|
89
services/docstore/Gruntfile.coffee
Normal file
89
services/docstore/Gruntfile.coffee
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
spawn = require("child_process").spawn
|
||||||
|
|
||||||
|
module.exports = (grunt) ->
|
||||||
|
grunt.initConfig
|
||||||
|
coffee:
|
||||||
|
app_src:
|
||||||
|
expand: true,
|
||||||
|
flatten: true,
|
||||||
|
cwd: "app"
|
||||||
|
src: ['coffee/*.coffee'],
|
||||||
|
dest: 'app/js/',
|
||||||
|
ext: '.js'
|
||||||
|
|
||||||
|
app:
|
||||||
|
src: "app.coffee"
|
||||||
|
dest: "app.js"
|
||||||
|
|
||||||
|
unit_tests:
|
||||||
|
expand: true
|
||||||
|
cwd: "test/unit/coffee"
|
||||||
|
src: ["**/*.coffee"]
|
||||||
|
dest: "test/unit/js/"
|
||||||
|
ext: ".js"
|
||||||
|
|
||||||
|
acceptance_tests:
|
||||||
|
expand: true
|
||||||
|
cwd: "test/acceptance/coffee"
|
||||||
|
src: ["**/*.coffee"]
|
||||||
|
dest: "test/acceptance/js/"
|
||||||
|
ext: ".js"
|
||||||
|
|
||||||
|
smoke_tests:
|
||||||
|
expand: true
|
||||||
|
cwd: "test/smoke/coffee"
|
||||||
|
src: ["**/*.coffee"]
|
||||||
|
dest: "test/smoke/js"
|
||||||
|
ext: ".js"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
app: ["app/js/"]
|
||||||
|
unit_tests: ["test/unit/js"]
|
||||||
|
acceptance_tests: ["test/acceptance/js"]
|
||||||
|
smoke_tests: ["test/smoke/js"]
|
||||||
|
|
||||||
|
execute:
|
||||||
|
app:
|
||||||
|
src: "app.js"
|
||||||
|
|
||||||
|
mochaTest:
|
||||||
|
unit:
|
||||||
|
options:
|
||||||
|
reporter: "spec"
|
||||||
|
src: ["test/unit/js/**/*.js"]
|
||||||
|
acceptance:
|
||||||
|
options:
|
||||||
|
reporter: "spec"
|
||||||
|
timeout: 40000
|
||||||
|
grep: grunt.option("grep")
|
||||||
|
src: ["test/acceptance/js/**/*.js"]
|
||||||
|
smoke:
|
||||||
|
options:
|
||||||
|
reported: "spec"
|
||||||
|
timeout: 10000
|
||||||
|
src: ["test/smoke/js/**/*.js"]
|
||||||
|
|
||||||
|
grunt.loadNpmTasks 'grunt-contrib-coffee'
|
||||||
|
grunt.loadNpmTasks 'grunt-contrib-clean'
|
||||||
|
grunt.loadNpmTasks 'grunt-mocha-test'
|
||||||
|
grunt.loadNpmTasks 'grunt-shell'
|
||||||
|
grunt.loadNpmTasks 'grunt-execute'
|
||||||
|
grunt.loadNpmTasks 'grunt-bunyan'
|
||||||
|
|
||||||
|
grunt.registerTask 'compile:app', ['clean:app', 'coffee:app', 'coffee:app_src', 'coffee:smoke_tests']
|
||||||
|
grunt.registerTask 'run', ['compile:app', 'bunyan', 'execute']
|
||||||
|
|
||||||
|
grunt.registerTask 'compile:unit_tests', ['clean:unit_tests', 'coffee:unit_tests']
|
||||||
|
grunt.registerTask 'test:unit', ['compile:app', 'compile:unit_tests', 'mochaTest:unit']
|
||||||
|
|
||||||
|
grunt.registerTask 'compile:acceptance_tests', ['clean:acceptance_tests', 'coffee:acceptance_tests']
|
||||||
|
grunt.registerTask 'test:acceptance', ['compile:acceptance_tests', 'mochaTest:acceptance']
|
||||||
|
|
||||||
|
grunt.registerTask 'compile:smoke_tests', ['clean:smoke_tests', 'coffee:smoke_tests']
|
||||||
|
grunt.registerTask 'test:smoke', ['compile:smoke_tests', 'mochaTest:smoke']
|
||||||
|
|
||||||
|
grunt.registerTask 'install', 'compile:app'
|
||||||
|
|
||||||
|
grunt.registerTask 'default', ['run']
|
||||||
|
|
||||||
|
|
23
services/docstore/app.coffee
Normal file
23
services/docstore/app.coffee
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Settings = require('settings-sharelatex')
|
||||||
|
logger = require('logger-sharelatex')
|
||||||
|
logger.initialize("docstore")
|
||||||
|
|
||||||
|
express = require('express')
|
||||||
|
HttpController = require "./app/js/HttpController"
|
||||||
|
|
||||||
|
app = express()
|
||||||
|
|
||||||
|
app.get '/project/:project_id/doc/:doc_id', HttpController.getDoc
|
||||||
|
|
||||||
|
app.get '/status', (req, res)->
|
||||||
|
res.send('docstore is alive')
|
||||||
|
|
||||||
|
app.use (error, req, res, next) ->
|
||||||
|
logger.error err: error, "request errored"
|
||||||
|
res.send(500, "Oops, something went wrong")
|
||||||
|
|
||||||
|
port = Settings.internal.docstore.port
|
||||||
|
host = Settings.internal.docstore.host
|
||||||
|
app.listen port, host, (error) ->
|
||||||
|
throw error if error?
|
||||||
|
logger.log("docstore listening on #{host}:#{port}")
|
31
services/docstore/app/coffee/DocManager.coffee
Normal file
31
services/docstore/app/coffee/DocManager.coffee
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
MongoManager = require "./MongoManager"
|
||||||
|
|
||||||
|
module.exports = DocManager =
|
||||||
|
getDoc: (project_id, doc_id, callback = (error, doc) ->) ->
|
||||||
|
MongoManager.findProject project_id, (error, project) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
return callback null, null if !project?
|
||||||
|
DocManager.findDocInProject project, doc_id, (error, doc) ->
|
||||||
|
return callback(error) if error?
|
||||||
|
return callback null, doc
|
||||||
|
|
||||||
|
findDocInProject: (project, doc_id, callback = (error, doc, mongoPath) ->) ->
|
||||||
|
result = @_findDocInFolder project.rootFolder[0], doc_id, "rootFolder.0"
|
||||||
|
if result?
|
||||||
|
callback null, result.doc, result.mongoPath
|
||||||
|
else
|
||||||
|
callback null, null, null
|
||||||
|
|
||||||
|
_findDocInFolder: (folder, doc_id, currentPath) ->
|
||||||
|
for doc, i in folder.docs or []
|
||||||
|
if doc._id.toString() == doc_id.toString()
|
||||||
|
return {
|
||||||
|
doc: doc
|
||||||
|
mongoPath: "#{currentPath}.docs.#{i}"
|
||||||
|
}
|
||||||
|
|
||||||
|
for childFolder, i in folder.folders or []
|
||||||
|
result = @_findDocInFolder childFolder, doc_id, "#{currentPath}.folders.#{i}"
|
||||||
|
return result if result?
|
||||||
|
|
||||||
|
return null
|
14
services/docstore/app/coffee/HttpController.coffee
Normal file
14
services/docstore/app/coffee/HttpController.coffee
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
DocManager = require "./DocManager"
|
||||||
|
logger = require "logger-sharelatex"
|
||||||
|
|
||||||
|
module.exports = HttpController =
|
||||||
|
getDoc: (req, res, next = (error) ->) ->
|
||||||
|
project_id = req.params.project_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 })
|
6
services/docstore/app/coffee/MongoManager.coffee
Normal file
6
services/docstore/app/coffee/MongoManager.coffee
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{db, ObjectId} = require "./mongojs"
|
||||||
|
|
||||||
|
module.exports = MongoManager =
|
||||||
|
findProject: (project_id, callback = (error, project) ->) ->
|
||||||
|
db.projects.find _id: ObjectId(project_id.toString()), {}, (error, projects = []) ->
|
||||||
|
callback error, projects[0]
|
7
services/docstore/app/coffee/mongojs.coffee
Normal file
7
services/docstore/app/coffee/mongojs.coffee
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Settings = require "settings-sharelatex"
|
||||||
|
mongojs = require "mongojs"
|
||||||
|
db = mongojs.connect(Settings.mongo.url, ["projects"])
|
||||||
|
module.exports =
|
||||||
|
db: db
|
||||||
|
ObjectId: mongojs.ObjectId
|
||||||
|
|
11
services/docstore/config/settings.defaults.coffee
Normal file
11
services/docstore/config/settings.defaults.coffee
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
http = require('http')
|
||||||
|
http.globalAgent.maxSockets = 300
|
||||||
|
|
||||||
|
module.exports =
|
||||||
|
internal:
|
||||||
|
docstore:
|
||||||
|
port: 3016
|
||||||
|
host: "localhost"
|
||||||
|
|
||||||
|
mongo:
|
||||||
|
url: 'mongodb://127.0.0.1/sharelatex'
|
25
services/docstore/package.json
Normal file
25
services/docstore/package.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "docstore-sharelatex",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "A CRUD API for handling text documents in projects",
|
||||||
|
"author": "ShareLaTeX <team@sharelatex>",
|
||||||
|
"dependencies": {
|
||||||
|
"settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master",
|
||||||
|
"logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master",
|
||||||
|
"mongojs": "0.9.11",
|
||||||
|
"express": "~4.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"grunt-execute": "~0.2.1",
|
||||||
|
"grunt-contrib-clean": "~0.5.0",
|
||||||
|
"grunt-shell": "~0.7.0",
|
||||||
|
"grunt-contrib-coffee": "~0.10.1",
|
||||||
|
"grunt-mocha-test": "~0.10.2",
|
||||||
|
"grunt": "~0.4.4",
|
||||||
|
"bunyan": "~0.22.3",
|
||||||
|
"grunt-bunyan": "~0.5.0",
|
||||||
|
"sinon": "~1.5.2",
|
||||||
|
"sandboxed-module": "~0.3.0",
|
||||||
|
"chai": "~1.9.1"
|
||||||
|
}
|
||||||
|
}
|
111
services/docstore/test/unit/coffee/DocManagerTests.coffee
Normal file
111
services/docstore/test/unit/coffee/DocManagerTests.coffee
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
sinon = require('sinon')
|
||||||
|
chai = require('chai')
|
||||||
|
chai.should()
|
||||||
|
expect = chai.expect
|
||||||
|
modulePath = require('path').join __dirname, '../../../app/js/DocManager'
|
||||||
|
ObjectId = require("mongojs").ObjectId
|
||||||
|
|
||||||
|
describe "DocManager", ->
|
||||||
|
beforeEach ->
|
||||||
|
@DocManager = SandboxedModule.require modulePath, requires:
|
||||||
|
"./MongoManager": @MongoManager = {}
|
||||||
|
@doc_id = ObjectId().toString()
|
||||||
|
@project_id = ObjectId().toString()
|
||||||
|
@callback = sinon.stub()
|
||||||
|
|
||||||
|
describe "getDoc", ->
|
||||||
|
describe "when the project exists", ->
|
||||||
|
beforeEach ->
|
||||||
|
@project = { name: "mock-project" }
|
||||||
|
@doc = { _id: @doc_id, lines: ["mock-lines"] }
|
||||||
|
@MongoManager.findProject = sinon.stub().callsArgWith(1, null, @project)
|
||||||
|
@DocManager.findDocInProject = sinon.stub().callsArgWith(2, null, @doc)
|
||||||
|
@DocManager.getDoc @project_id, @doc_id, @callback
|
||||||
|
|
||||||
|
it "should get the project from the database", ->
|
||||||
|
@MongoManager.findProject
|
||||||
|
.calledWith(@project_id)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should find the doc in the project", ->
|
||||||
|
@DocManager.findDocInProject
|
||||||
|
.calledWith(@project, @doc_id)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should return the doc", ->
|
||||||
|
@callback.calledWith(null, @doc).should.equal true
|
||||||
|
|
||||||
|
describe "when the project does not exist", ->
|
||||||
|
beforeEach ->
|
||||||
|
@MongoManager.findProject = sinon.stub().callsArgWith(1, null, @null)
|
||||||
|
@DocManager.findDocInProject = sinon.stub()
|
||||||
|
@DocManager.getDoc @project_id, @doc_id, @callback
|
||||||
|
|
||||||
|
it "should not try to find the doc in the project", ->
|
||||||
|
@DocManager.findDocInProject.called.should.equal false
|
||||||
|
|
||||||
|
it "should return null", ->
|
||||||
|
@callback.calledWith(null, null).should.equal true
|
||||||
|
|
||||||
|
describe "findDocInProject", ->
|
||||||
|
it "should find the doc when it is in the root folder", (done) ->
|
||||||
|
@DocManager.findDocInProject {
|
||||||
|
rootFolder: [{
|
||||||
|
docs: [{
|
||||||
|
_id: ObjectId(@doc_id)
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}, @doc_id, (error, doc, mongoPath) =>
|
||||||
|
expect(doc).to.deep.equal { _id: ObjectId(@doc_id) }
|
||||||
|
mongoPath.should.equal "rootFolder.0.docs.0"
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should find the doc when it is in a sub folder", (done) ->
|
||||||
|
@DocManager.findDocInProject {
|
||||||
|
rootFolder: [{
|
||||||
|
folders: [{
|
||||||
|
docs: [{
|
||||||
|
_id: ObjectId(@doc_id)
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}, @doc_id, (error, doc, mongoPath) =>
|
||||||
|
expect(doc).to.deep.equal { _id: ObjectId(@doc_id) }
|
||||||
|
mongoPath.should.equal "rootFolder.0.folders.0.docs.0"
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should find the doc when it there are other docs", (done) ->
|
||||||
|
@DocManager.findDocInProject {
|
||||||
|
rootFolder: [{
|
||||||
|
folders: [{
|
||||||
|
docs: [{
|
||||||
|
_id: ObjectId()
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
docs: [{
|
||||||
|
_id: ObjectId()
|
||||||
|
}, {
|
||||||
|
_id: ObjectId(@doc_id)
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
docs: [{
|
||||||
|
_id: ObjectId()
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}, @doc_id, (error, doc, mongoPath) =>
|
||||||
|
expect(doc).to.deep.equal { _id: ObjectId(@doc_id) }
|
||||||
|
mongoPath.should.equal "rootFolder.0.folders.1.docs.1"
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should return null when the doc doesn't exist", (done) ->
|
||||||
|
@DocManager.findDocInProject {
|
||||||
|
rootFolder: [{
|
||||||
|
folders: [{
|
||||||
|
docs: []
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}, @doc_id, (error, doc, mongoPath) =>
|
||||||
|
expect(doc).to.be.null
|
||||||
|
expect(mongoPath).to.be.null
|
||||||
|
done()
|
|
@ -0,0 +1,53 @@
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
sinon = require('sinon')
|
||||||
|
chai = require('chai')
|
||||||
|
chai.should()
|
||||||
|
expect = chai.expect
|
||||||
|
modulePath = require('path').join __dirname, '../../../app/js/HttpController'
|
||||||
|
|
||||||
|
describe "HttpController", ->
|
||||||
|
beforeEach ->
|
||||||
|
@HttpController = SandboxedModule.require modulePath, requires:
|
||||||
|
"./DocManager": @DocManager = {}
|
||||||
|
"logger-sharelatex": @logger = { log: sinon.stub() }
|
||||||
|
@res = { send: sinon.stub() }
|
||||||
|
@req = {}
|
||||||
|
@next = sinon.stub()
|
||||||
|
@project_id = "mock-project-id"
|
||||||
|
@doc_id = "mock-doc-id"
|
||||||
|
@doc = {
|
||||||
|
_id: @doc_id
|
||||||
|
lines: ["mock", "lines"]
|
||||||
|
}
|
||||||
|
|
||||||
|
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.send
|
||||||
|
.calledWith(JSON.stringify(lines: @doc.lines))
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
describe "when the doc does not exist", ->
|
||||||
|
beforeEach ->
|
||||||
|
@req.params =
|
||||||
|
project_id: @project_id
|
||||||
|
doc_id: @doc_id
|
||||||
|
@DocManager.getDoc = sinon.stub().callsArgWith(2, null, null)
|
||||||
|
@HttpController.getDoc @req, @res, @next
|
||||||
|
|
||||||
|
it "should return a 404", ->
|
||||||
|
@res.send
|
||||||
|
.calledWith(404)
|
||||||
|
.should.equal true
|
27
services/docstore/test/unit/coffee/MongoManagerTests.coffee
Normal file
27
services/docstore/test/unit/coffee/MongoManagerTests.coffee
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
sinon = require('sinon')
|
||||||
|
require('chai').should()
|
||||||
|
modulePath = require('path').join __dirname, '../../../app/js/MongoManager'
|
||||||
|
ObjectId = require("mongojs").ObjectId
|
||||||
|
|
||||||
|
describe "MongoManager", ->
|
||||||
|
beforeEach ->
|
||||||
|
@MongoManager = SandboxedModule.require modulePath, requires:
|
||||||
|
"./mongojs":
|
||||||
|
db: @db = { projects: {} }
|
||||||
|
ObjectId: ObjectId
|
||||||
|
@project_id = ObjectId().toString()
|
||||||
|
@callback = sinon.stub()
|
||||||
|
|
||||||
|
describe "findProject", ->
|
||||||
|
beforeEach ->
|
||||||
|
@project = { name: "mock-project" }
|
||||||
|
@db.projects.find = sinon.stub().callsArgWith(2, null, [@project])
|
||||||
|
@MongoManager.findProject @project_id, @callback
|
||||||
|
|
||||||
|
it "should find the project without the doc lines", ->
|
||||||
|
@db.projects.find
|
||||||
|
.calledWith({
|
||||||
|
_id: ObjectId(@project_id)
|
||||||
|
}, {})
|
||||||
|
.should.equal true
|
Loading…
Reference in a new issue