[WEB + CLSI] Download as docx file feature (#32851)

* using CLSI logic for fetching the project contents and skip the .zip export

* Use unique conversion directory for project-to-docx export to avoid corrupting the shared compile
  directory when a compile runs concurrently

* Remove X-Accel-Buffering header — not needed as CLSI does not run behind nginx

* moving log before sending the data

* Return CLSI stream directly instead of buffering to disk on web

  Previously convertProjectToDocx wrote the CLSI response to a temp file
  on disk, then the controller read it back to stream to the client.
  Now the stream is returned directly and piped to the response,
  avoiding unnecessary disk I/O on the web server.

* Use href redirect for docx export instead of fetching blob into memory

* making functions and files more generic so they can be used in future for other documents exports as well

* adding export-docx split test

* adding unit tests

* adding cypress E2E test

* format:fix

* renaming the route to download from convert

* adding new icon for export docx button

* format:fix

* remove unused showExportDocumentErrorToast export and adding guard against invalid Content-Length header from CLSI

* format:fix

* refactor(clsi): move promisify(parse) into RequestParser

* refactor: generic conversion endpoint with type as route
  param

* refactor: use type→extension map for validated conversion types

* refactor(clsi): remove --standalone flag and fix rejection test

* fixing the href in cypress test

* renaming function

* adding type to Metrics.inc

* fix: rename exportProjectDocument, add WithLock wrapper and metrics type label

* format:fix

* fix: hide docx export from anonymous users and add WithLock wrapper

* format fix

* remove redundant Content-Length validation from DocumentConversionManager

* format:fix

* removing trailing icon

GitOrigin-RevId: e9764fefac2c4b625d23be9e942ea4a8b283c70d
This commit is contained in:
Davinder Singh
2026-04-23 11:44:38 +01:00
committed by Copybot
parent b6ec7945f4
commit be5a7b56c8
20 changed files with 772 additions and 41 deletions

View File

@@ -250,4 +250,89 @@ describe('ConversionManager', function () {
})
})
})
describe('convertLaTeXToDocumentInDirWithLock', function () {
describe('successfully', function () {
beforeEach(async function (ctx) {
ctx.compileDir = '/compiles/test-compile-dir'
ctx.rootDocPath = 'main.tex'
ctx.type = 'docx'
ctx.extension = 'docx'
ctx.result =
await ctx.ConversionManager.promises.convertLaTeXToDocumentInDirWithLock(
ctx.conversionId,
ctx.compileDir,
ctx.rootDocPath,
ctx.type,
ctx.extension
)
})
it('should acquire a lock on the compile dir', function (ctx) {
sinon.assert.calledWith(ctx.LockManager.acquire, ctx.compileDir)
})
it('should release the lock', function (ctx) {
sinon.assert.called(ctx.lock.release)
})
it('should run pandoc with correct arguments', function (ctx) {
expect(ctx.CommandRunner.promises.run.callCount).toBe(1)
expect(ctx.CommandRunner.promises.run.firstCall.args).toEqual([
ctx.conversionId,
[
'pandoc',
ctx.rootDocPath,
'--output',
`output-uuid.${ctx.extension}`,
'--from',
'latex',
'--to',
ctx.type,
'--resource-path=.',
],
ctx.compileDir,
ctx.Settings.pandocImage,
60_000,
{},
'conversions',
])
})
it('should convert conversion timeout to milliseconds', function (ctx) {
expect(ctx.CommandRunner.promises.run.firstCall.args[4]).toBe(60_000)
})
it('should return path to the output document', function (ctx) {
expect(ctx.result).toBe(
Path.join(ctx.compileDir, `output-uuid.${ctx.extension}`)
)
})
})
describe('when pandoc fails (non-zero exit code)', function () {
it('should reject with an error and release the lock', async function (ctx) {
ctx.compileDir = '/compiles/test-compile-dir'
ctx.CommandRunner.promises.run.resolves({
stdout: 'mock-stdout',
stderr: 'mock-stderr',
exitCode: 1,
})
await expect(
ctx.ConversionManager.promises.convertLaTeXToDocumentInDirWithLock(
ctx.conversionId,
ctx.compileDir,
'main.tex',
'docx',
'docx'
)
).to.be.rejectedWith('pandoc latex-to-document conversion failed')
sinon.assert.called(ctx.lock.release)
})
})
})
})