1
0
Fork 0
mirror of https://github.com/overleaf/overleaf.git synced 2025-04-14 03:16:46 +00:00

splice state management into ResourceStateManager

This commit is contained in:
Brian Gough 2017-08-18 10:22:17 +01:00
parent 96b801b093
commit a84c884fc9
3 changed files with 79 additions and 49 deletions

View file

@ -0,0 +1,46 @@
Path = require "path"
fs = require "fs"
mkdirp = require "mkdirp"
logger = require "logger-sharelatex"
settings = require("settings-sharelatex")
Errors = require "./Errors"
module.exports = ResourceStateManager =
# The sync state is an identifier which must match for an
# incremental update to be allowed.
#
# The initial value is passed in and stored on a full
# compile.
#
# Subsequent incremental compiles must come with the same value - if
# not they will be rejected with a 409 Conflict response.
#
# An incremental compile can only update existing files with new
# content. The sync state identifier must change if any docs or
# files are moved, added, deleted or renamed.
SYNC_STATE_FILE: ".project-sync-state"
saveProjectStateHash: (state, basePath, callback) ->
stateFile = Path.join(basePath, @SYNC_STATE_FILE)
if not state? # remove the file if no state passed in
logger.log state:state, basePath:basePath, "clearing sync state"
fs.unlink stateFile, (err) ->
if err? and err.code isnt 'ENOENT'
return callback(err)
else
return callback()
else
logger.log state:state, basePath:basePath, "writing sync state"
fs.writeFile stateFile, state, {encoding: 'ascii'}, callback
checkProjectStateHashMatches: (state, basePath, callback) ->
stateFile = Path.join(basePath, @SYNC_STATE_FILE)
fs.readFile stateFile, {encoding:'ascii'}, (err, oldState) ->
if err? and err.code isnt 'ENOENT'
return callback(err)
else if state isnt oldState
return callback new Errors.FilesOutOfSyncError("invalid state for incremental update")
else if state is oldState
callback(null)

View file

@ -4,9 +4,9 @@ fs = require "fs"
async = require "async"
mkdirp = require "mkdirp"
OutputFileFinder = require "./OutputFileFinder"
ResourceStateManager = require "./ResourceStateManager"
ResourceListManager = require "./ResourceListManager"
Metrics = require "./Metrics"
Errors = require "./Errors"
logger = require "logger-sharelatex"
settings = require("settings-sharelatex")
@ -16,9 +16,8 @@ module.exports = ResourceWriter =
syncResourcesToDisk: (request, basePath, callback = (error, resourceList) ->) ->
if request.syncType is "incremental"
ResourceWriter.checkSyncState request.syncState, basePath, (error, syncStateOk) ->
logger.log syncState: request.syncState, result:syncStateOk, "checked state on incremental request"
return callback new Errors.FilesOutOfSyncError("invalid state for incremental update") if not syncStateOk
ResourceStateManager.checkProjectStateHashMatches request.syncState, basePath, (error) ->
return callback(error) if error?
ResourceListManager.loadResourceList basePath, (error, resourceList) ->
return callback(error) if error?
ResourceWriter._removeExtraneousFiles resourceList, basePath, (error) =>
@ -26,53 +25,15 @@ module.exports = ResourceWriter =
ResourceWriter.saveIncrementalResourcesToDisk request.project_id, request.resources, basePath, (error) ->
return callback(error) if error?
callback(null, resourceList)
else
@saveAllResourcesToDisk request.project_id, request.resources, basePath, (error) ->
return callback(error) if error?
ResourceWriter.storeSyncState request.syncState, basePath, (error) ->
ResourceStateManager.saveProjectStateHash request.syncState, basePath, (error) ->
return callback(error) if error?
ResourceListManager.saveResourceList request.resources, basePath, (error) =>
return callback(error) if error?
callback(null, request.resources)
# The sync state is an identifier which must match for an
# incremental update to be allowed.
#
# The initial value is passed in and stored on a full
# compile.
#
# Subsequent incremental compiles must come with the same value - if
# not they will be rejected with a 409 Conflict response.
#
# An incremental compile can only update existing files with new
# content. The sync state identifier must change if any docs or
# files are moved, added, deleted or renamed.
SYNC_STATE_FILE: ".project-sync-state"
storeSyncState: (state, basePath, callback) ->
stateFile = Path.join(basePath, @SYNC_STATE_FILE)
if not state? # remove the file if no state passed in
logger.log state:state, basePath:basePath, "clearing sync state"
fs.unlink stateFile, (err) ->
if err? and err.code isnt 'ENOENT'
return callback(err)
else
return callback()
else
logger.log state:state, basePath:basePath, "writing sync state"
fs.writeFile stateFile, state, {encoding: 'ascii'}, callback
checkSyncState: (state, basePath, callback) ->
stateFile = Path.join(basePath, @SYNC_STATE_FILE)
fs.readFile stateFile, {encoding:'ascii'}, (err, oldState) ->
if err? and err.code isnt 'ENOENT'
return callback(err)
else
# return true if state matches, false otherwise (including file not existing)
callback(null, if state is oldState then true else false)
saveIncrementalResourcesToDisk: (project_id, resources, basePath, callback = (error) ->) ->
@_createDirectory basePath, (error) =>
return callback(error) if error?

View file

@ -11,6 +11,7 @@ describe "ResourceWriter", ->
mkdir: sinon.stub().callsArg(1)
unlink: sinon.stub().callsArg(1)
"./ResourceListManager": @ResourceListManager = {}
"./ResourceStateManager": @ResourceStateManager = {}
"wrench": @wrench = {}
"./UrlCache" : @UrlCache = {}
"mkdirp" : @mkdirp = sinon.stub().callsArg(1)
@ -32,8 +33,8 @@ describe "ResourceWriter", ->
]
@ResourceWriter._writeResourceToDisk = sinon.stub().callsArg(3)
@ResourceWriter._removeExtraneousFiles = sinon.stub().callsArg(2)
@ResourceWriter.checkSyncState = sinon.stub().callsArg(2)
@ResourceWriter.storeSyncState = sinon.stub().callsArg(2)
@ResourceStateManager.checkProjectStateHashMatches = sinon.stub().callsArg(2)
@ResourceStateManager.saveProjectStateHash = sinon.stub().callsArg(2)
@ResourceListManager.saveResourceList = sinon.stub().callsArg(2)
@ResourceListManager.loadResourceList = sinon.stub().callsArg(1)
@ResourceWriter.syncResourcesToDisk({
@ -54,7 +55,7 @@ describe "ResourceWriter", ->
.should.equal true
it "should store the sync state", ->
@ResourceWriter.storeSyncState
@ResourceStateManager.saveProjectStateHash
.calledWith(@syncState, @basePath)
.should.equal true
@ -73,8 +74,8 @@ describe "ResourceWriter", ->
]
@ResourceWriter._writeResourceToDisk = sinon.stub().callsArg(3)
@ResourceWriter._removeExtraneousFiles = sinon.stub().callsArg(2)
@ResourceWriter.checkSyncState = sinon.stub().callsArgWith(2, null, true)
@ResourceWriter.storeSyncState = sinon.stub().callsArg(2)
@ResourceStateManager.checkProjectStateHashMatches = sinon.stub().callsArg(2)
@ResourceStateManager.saveProjectStateHash = sinon.stub().callsArg(2)
@ResourceListManager.saveResourceList = sinon.stub().callsArg(2)
@ResourceListManager.loadResourceList = sinon.stub().callsArgWith(1, null, @resources)
@ResourceWriter.syncResourcesToDisk({
@ -85,7 +86,7 @@ describe "ResourceWriter", ->
}, @basePath, @callback)
it "should check the sync state matches", ->
@ResourceWriter.checkSyncState
@ResourceStateManager.checkProjectStateHashMatches
.calledWith(@syncState, @basePath)
.should.equal true
@ -103,6 +104,28 @@ describe "ResourceWriter", ->
it "should call the callback", ->
@callback.called.should.equal true
describe "syncResourcesToDisk on an incremental update when the state does not match", ->
beforeEach ->
@resources = [
"resource-1-mock"
]
@ResourceStateManager.checkProjectStateHashMatches = sinon.stub().callsArgWith(2, @error = new Error())
@ResourceWriter.syncResourcesToDisk({
project_id: @project_id,
syncType: "incremental",
syncState: @syncState = "1234567890abcdef",
resources: @resources
}, @basePath, @callback)
it "should check whether the sync state matches", ->
@ResourceStateManager.checkProjectStateHashMatches
.calledWith(@syncState, @basePath)
.should.equal true
it "should call the callback with an error", ->
@callback.calledWith(@error).should.equal true
describe "_removeExtraneousFiles", ->
beforeEach ->
@output_files = [{