mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 03:43:39 -05:00
[web] support for reverting binary files (#18033)
* [web] revert binary file * use addEntityWithName if file was deleted * todo comments * only show Revert file in ui even if deleted * use _revertBinaryFile function * emit new ids when reverting * format:fix * await emitToRoom calls * use EditorController.upsertFile * remove _revertBinaryFile function * binary file check * mock importFile method in tests * move findElementByPath stub * debug ci error * resolve with empty object as file * fix tests * remove await before expect() * format:fix * test when binary file exists and when it does not * use "file-revert" for source * [web] revert existing file without ranges support (#18107) * [web] revert existing file without ranges support * ignore document_updated_externally if file-revert * fix test GitOrigin-RevId: a5e0c83a7635bc7d934dec9debe916bdd4beb51e
This commit is contained in:
parent
3b2e60ece7
commit
218a4538c1
5 changed files with 106 additions and 40 deletions
|
@ -8,6 +8,7 @@ const moment = require('moment')
|
||||||
const { callbackifyAll } = require('@overleaf/promise-utils')
|
const { callbackifyAll } = require('@overleaf/promise-utils')
|
||||||
const { fetchJson } = require('@overleaf/fetch-utils')
|
const { fetchJson } = require('@overleaf/fetch-utils')
|
||||||
const ProjectLocator = require('../Project/ProjectLocator')
|
const ProjectLocator = require('../Project/ProjectLocator')
|
||||||
|
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
||||||
|
|
||||||
const RestoreManager = {
|
const RestoreManager = {
|
||||||
async restoreFileFromV2(userId, projectId, version, pathname) {
|
async restoreFileFromV2(userId, projectId, version, pathname) {
|
||||||
|
@ -42,6 +43,7 @@ const RestoreManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async revertFile(userId, projectId, version, pathname) {
|
async revertFile(userId, projectId, version, pathname) {
|
||||||
|
const source = 'file-revert'
|
||||||
const fsPath = await RestoreManager._writeFileVersionToDisk(
|
const fsPath = await RestoreManager._writeFileVersionToDisk(
|
||||||
projectId,
|
projectId,
|
||||||
version,
|
version,
|
||||||
|
@ -50,34 +52,53 @@ const RestoreManager = {
|
||||||
const basename = Path.basename(pathname)
|
const basename = Path.basename(pathname)
|
||||||
let dirname = Path.dirname(pathname)
|
let dirname = Path.dirname(pathname)
|
||||||
if (dirname === '.') {
|
if (dirname === '.') {
|
||||||
// no directory
|
// root directory
|
||||||
dirname = ''
|
dirname = '/'
|
||||||
}
|
}
|
||||||
const parentFolderId = await RestoreManager._findOrCreateFolder(
|
const parentFolderId = await RestoreManager._findOrCreateFolder(
|
||||||
projectId,
|
projectId,
|
||||||
dirname
|
dirname
|
||||||
)
|
)
|
||||||
let fileExists = true
|
const file = await ProjectLocator.promises
|
||||||
try {
|
.findElementByPath({
|
||||||
// TODO: Is there a better way of doing this?
|
project_id: projectId,
|
||||||
await ProjectLocator.promises.findElementByPath({
|
|
||||||
projectId,
|
|
||||||
path: pathname,
|
path: pathname,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
.catch(() => null)
|
||||||
fileExists = false
|
|
||||||
}
|
|
||||||
if (fileExists) {
|
|
||||||
throw new Errors.InvalidError('File already exists')
|
|
||||||
}
|
|
||||||
|
|
||||||
const importInfo = await FileSystemImportManager.promises.importFile(
|
const importInfo = await FileSystemImportManager.promises.importFile(
|
||||||
fsPath,
|
fsPath,
|
||||||
pathname
|
pathname
|
||||||
)
|
)
|
||||||
if (importInfo.type !== 'doc') {
|
if (importInfo.type === 'file') {
|
||||||
// TODO: Handle binary files
|
const newFile = await EditorController.promises.upsertFile(
|
||||||
throw new Errors.InvalidError('File is not editable')
|
projectId,
|
||||||
|
parentFolderId,
|
||||||
|
basename,
|
||||||
|
fsPath,
|
||||||
|
file?.element?.linkedFileData,
|
||||||
|
source,
|
||||||
|
userId
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: newFile._id,
|
||||||
|
type: importInfo.type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
await DocumentUpdaterHandler.promises.setDocument(
|
||||||
|
projectId,
|
||||||
|
file.element._id,
|
||||||
|
userId,
|
||||||
|
importInfo.lines,
|
||||||
|
source
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
_id: file.element._id,
|
||||||
|
type: importInfo.type,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ranges = await RestoreManager._getRangesFromHistory(
|
const ranges = await RestoreManager._getRangesFromHistory(
|
||||||
|
|
|
@ -12,7 +12,7 @@ export interface Meta {
|
||||||
start_ts: number
|
start_ts: number
|
||||||
end_ts: number
|
end_ts: number
|
||||||
type?: 'external' // TODO
|
type?: 'external' // TODO
|
||||||
source?: 'git-bridge' // TODO
|
source?: 'git-bridge' | 'file-revert' // TODO
|
||||||
origin?: {
|
origin?: {
|
||||||
kind:
|
kind:
|
||||||
| 'dropbox'
|
| 'dropbox'
|
||||||
|
|
|
@ -283,6 +283,9 @@ export const EditorManagerProvider: FC = ({ children }) => {
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (update.meta.source === 'file-revert') {
|
||||||
|
return
|
||||||
|
}
|
||||||
showGenericMessageModal(
|
showGenericMessageModal(
|
||||||
t('document_updated_externally'),
|
t('document_updated_externally'),
|
||||||
t('document_updated_externally_detail')
|
t('document_updated_externally_detail')
|
||||||
|
|
|
@ -442,6 +442,9 @@ export default EditorManager = (function () {
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (update?.meta?.source === 'file-revert') {
|
||||||
|
return
|
||||||
|
}
|
||||||
return this.ide.showGenericMessageModal(
|
return this.ide.showGenericMessageModal(
|
||||||
'Document Updated Externally',
|
'Document Updated Externally',
|
||||||
'This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions please look in the history.'
|
'This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions please look in the history.'
|
||||||
|
|
|
@ -23,6 +23,8 @@ describe('RestoreManager', function () {
|
||||||
promises: {},
|
promises: {},
|
||||||
}),
|
}),
|
||||||
'../Project/ProjectLocator': (this.ProjectLocator = { promises: {} }),
|
'../Project/ProjectLocator': (this.ProjectLocator = { promises: {} }),
|
||||||
|
'../DocumentUpdater/DocumentUpdaterHandler':
|
||||||
|
(this.DocumentUpdaterHandler = { promises: {} }),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.user_id = 'mock-user-id'
|
this.user_id = 'mock-user-id'
|
||||||
|
@ -217,41 +219,78 @@ describe('RestoreManager', function () {
|
||||||
describe('with an existing file in the current project', function () {
|
describe('with an existing file in the current project', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.pathname = 'foo.tex'
|
this.pathname = 'foo.tex'
|
||||||
this.ProjectLocator.promises.findElementByPath = sinon.stub().resolves()
|
this.FileSystemImportManager.promises.importFile = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves({ type: 'doc' })
|
||||||
|
this.ProjectLocator.promises.findElementByPath = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves({ type: 'doc', element: { _id: 'mock-file-id' } })
|
||||||
|
this.FileSystemImportManager.promises.importFile = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves({ type: 'doc', lines: ['foo', 'bar', 'baz'] })
|
||||||
|
this.DocumentUpdaterHandler.promises.setDocument = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should reject', function () {
|
it('should call setDocument in document updater and revert file', async function () {
|
||||||
expect(
|
const revertRes = await this.RestoreManager.promises.revertFile(
|
||||||
this.RestoreManager.promises.revertFile(
|
|
||||||
this.user_id,
|
this.user_id,
|
||||||
this.project_id,
|
this.project_id,
|
||||||
this.version,
|
this.version,
|
||||||
this.pathname
|
this.pathname
|
||||||
)
|
)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
this.DocumentUpdaterHandler.promises.setDocument
|
||||||
|
).to.have.been.calledWith(
|
||||||
|
this.project_id,
|
||||||
|
'mock-file-id',
|
||||||
|
this.user_id,
|
||||||
|
['foo', 'bar', 'baz'],
|
||||||
|
'file-revert'
|
||||||
)
|
)
|
||||||
.to.eventually.be.rejectedWith('File already exists')
|
expect(revertRes).to.deep.equal({ _id: 'mock-file-id', type: 'doc' })
|
||||||
.and.be.instanceOf(Errors.InvalidError)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when reverting a binary file', function () {
|
describe('when reverting a binary file', function () {
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
this.pathname = 'foo.png'
|
this.pathname = 'foo.png'
|
||||||
this.FileSystemImportManager.promises.importFile = sinon
|
this.FileSystemImportManager.promises.importFile = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.resolves({ type: 'binary' })
|
.resolves({ type: 'file' })
|
||||||
|
this.EditorController.promises.upsertFile = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves({ _id: 'mock-file-id', type: 'file' })
|
||||||
})
|
})
|
||||||
it('should reject', function () {
|
|
||||||
expect(
|
it('should return the created entity if file exists', async function () {
|
||||||
this.RestoreManager.promises.revertFile(
|
this.ProjectLocator.promises.findElementByPath = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves({ type: 'file' })
|
||||||
|
|
||||||
|
const revertRes = await this.RestoreManager.promises.revertFile(
|
||||||
this.user_id,
|
this.user_id,
|
||||||
this.project_id,
|
this.project_id,
|
||||||
this.version,
|
this.version,
|
||||||
this.pathname
|
this.pathname
|
||||||
)
|
)
|
||||||
|
|
||||||
|
expect(revertRes).to.deep.equal({ _id: 'mock-file-id', type: 'file' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the created entity if file does not exists', async function () {
|
||||||
|
this.ProjectLocator.promises.findElementByPath = sinon.stub().rejects()
|
||||||
|
|
||||||
|
const revertRes = await this.RestoreManager.promises.revertFile(
|
||||||
|
this.user_id,
|
||||||
|
this.project_id,
|
||||||
|
this.version,
|
||||||
|
this.pathname
|
||||||
)
|
)
|
||||||
.to.eventually.be.rejectedWith('File is not editable')
|
|
||||||
.and.be.instanceOf(Errors.InvalidError)
|
expect(revertRes).to.deep.equal({ _id: 'mock-file-id', type: 'file' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue