Преглед изворни кода

[web] inline trivial mongo queries when creating project from template (#30601)

GitOrigin-RevId: 7aefee892d491b1ec191d07bf1871317b897dec8
Jakob Ackermann пре 7 месеци
родитељ
комит
292230b1db

+ 1 - 0
services/web/app/src/Features/Project/ProjectEntityUpdateHandler.mjs

@@ -221,6 +221,7 @@ async function setRootDoc(projectId, newRootDocID) {
       'invalid file extension for root doc'
     )
   }
+  return newRootDocID
 }
 
 async function unsetRootDoc(projectId) {

+ 28 - 7
services/web/app/src/Features/Project/ProjectOptionsHandler.mjs

@@ -9,22 +9,35 @@ const safeCompilers = ['xelatex', 'pdflatex', 'latex', 'lualatex']
 const { ReturnDocument } = mongodb
 
 const ProjectOptionsHandler = {
-  async setCompiler(projectId, compiler) {
-    if (!compiler) {
-      return
-    }
+  /**
+   * @param {string} compiler
+   * @return {string}
+   */
+  normalizeCompiler(compiler) {
     compiler = compiler.toLowerCase()
     if (!safeCompilers.includes(compiler)) {
       throw new Error(`invalid compiler: ${compiler}`)
     }
+    return compiler
+  },
+
+  async setCompiler(projectId, compiler) {
+    if (!compiler) {
+      return
+    }
+    compiler = ProjectOptionsHandler.normalizeCompiler(compiler)
     const conditions = { _id: projectId }
     const update = { compiler }
     return Project.updateOne(conditions, update, {})
   },
 
-  async setImageName(projectId, imageName) {
+  /**
+   * @param {string} imageName
+   * @return {string | undefined}
+   */
+  normalizeImageName(imageName) {
     if (!imageName || !Array.isArray(settings.allowedImageNames)) {
-      return
+      return undefined
     }
     imageName = imageName.toLowerCase()
     const isAllowed = settings.allowedImageNames.find(
@@ -33,8 +46,16 @@ const ProjectOptionsHandler = {
     if (!isAllowed) {
       throw new Error(`invalid imageName: ${imageName}`)
     }
+    return settings.imageRoot + '/' + imageName
+  },
+
+  async setImageName(projectId, imageName) {
+    imageName = ProjectOptionsHandler.normalizeImageName(imageName)
+    if (!imageName) {
+      return
+    }
     const conditions = { _id: projectId }
-    const update = { imageName: settings.imageRoot + '/' + imageName }
+    const update = { imageName }
     return Project.updateOne(conditions, update, {})
   },
 

+ 16 - 35
services/web/app/src/Features/Templates/TemplatesManager.mjs

@@ -1,4 +1,3 @@
-import { Project } from '../../models/Project.mjs'
 import ProjectDetailsHandler from '../Project/ProjectDetailsHandler.mjs'
 import ProjectOptionsHandlerModule from '../Project/ProjectOptionsHandler.mjs'
 import ProjectRootDocManagerModule from '../Project/ProjectRootDocManager.mjs'
@@ -31,6 +30,11 @@ const TemplatesManager = {
     userId,
     imageName
   ) {
+    compiler = ProjectOptionsHandler.normalizeCompiler(compiler || 'pdflatex')
+    imageName = ProjectOptionsHandler.normalizeImageName(
+      imageName || 'wl_texlive:2018.1'
+    )
+
     const zipUrl = `${settings.apis.v1.url}/api/v1/overleaf/templates/${templateVersionId}`
     const zipReq = await fetchStreamWithResponse(zipUrl, {
       basicAuth: {
@@ -47,7 +51,11 @@ const TemplatesManager = {
       const attributes = {
         fromV1TemplateId: templateId,
         fromV1TemplateVersionId: templateVersionId,
+        compiler,
+        imageName,
       }
+      if (brandVariationId) attributes.brandVariationId = brandVariationId
+
       await pipeline(zipReq.stream, writeStream)
 
       if (zipReq.response.status !== 200) {
@@ -76,16 +84,7 @@ const TemplatesManager = {
         )
       })
 
-      await TemplatesManager._setCompiler(project._id, compiler)
-      await TemplatesManager._setImage(project._id, imageName)
-      await TemplatesManager._setMainFile(project._id, mainFile)
-      await TemplatesManager._setBrandVariationId(project._id, brandVariationId)
-
-      const update = {
-        fromV1TemplateId: templateId,
-        fromV1TemplateVersionId: templateVersionId,
-      }
-      await Project.updateOne({ _id: project._id }, update, {})
+      await TemplatesManager._setMainFile(project, mainFile)
 
       await prepareClsiCacheInBackground
 
@@ -95,33 +94,15 @@ const TemplatesManager = {
     }
   },
 
-  async _setCompiler(projectId, compiler) {
-    if (compiler == null) {
-      return
-    }
-    await ProjectOptionsHandler.setCompiler(projectId, compiler)
-  },
-
-  async _setImage(projectId, imageName) {
-    if (!imageName) {
-      imageName = 'wl_texlive:2018.1'
-    }
-
-    await ProjectOptionsHandler.setImageName(projectId, imageName)
-  },
-
-  async _setMainFile(projectId, mainFile) {
+  async _setMainFile(project, mainFile) {
     if (mainFile == null) {
       return
     }
-    await ProjectRootDocManager.setRootDocFromName(projectId, mainFile)
-  },
-
-  async _setBrandVariationId(projectId, brandVariationId) {
-    if (brandVariationId == null) {
-      return
-    }
-    await ProjectOptionsHandler.setBrandVariationId(projectId, brandVariationId)
+    const rootDocId = await ProjectRootDocManager.setRootDocFromName(
+      project._id,
+      mainFile
+    )
+    if (rootDocId) project.rootDoc_id = rootDocId
   },
 
   async fetchFromV1(templateId) {

+ 5 - 1
services/web/app/src/Features/Uploads/ProjectUploadManager.mjs

@@ -87,7 +87,11 @@ async function createProjectFromZipArchiveWithName(
 
     try {
       await _initializeProjectWithZipContents(ownerId, project, contentsPath)
-      await ProjectRootDocManager.promises.setRootDocAutomatically(project._id)
+      const rootDocId =
+        await ProjectRootDocManager.promises.setRootDocAutomatically(
+          project._id
+        )
+      if (rootDocId) project.rootDoc_id = rootDocId
     } catch (err) {
       // no need to wait for the cleanup here
       ProjectDeleter.promises

+ 20 - 33
services/web/test/unit/src/Templates/TemplatesManager.test.mjs

@@ -47,6 +47,8 @@ describe('TemplatesManager', function () {
         setCompiler: sinon.stub().resolves(),
         setImageName: sinon.stub().resolves(),
         setBrandVariationId: sinon.stub().resolves(),
+        normalizeCompiler: sinon.stub().returnsArg(0),
+        normalizeImageName: sinon.stub().returnsArg(0),
       },
     }
     ctx.ProjectRootDocManager = {
@@ -182,6 +184,9 @@ describe('TemplatesManager', function () {
           {
             fromV1TemplateId: ctx.templateId,
             fromV1TemplateVersionId: ctx.templateVersionId,
+            compiler: ctx.compiler,
+            imageName: ctx.imageName,
+            brandVariationId: ctx.brandVariationId,
           }
         )
       })
@@ -190,33 +195,11 @@ describe('TemplatesManager', function () {
         ctx.fs.promises.unlink.should.have.been.calledWith(ctx.dumpPath)
       })
 
-      it('should set project options when passed', function (ctx) {
-        ctx.ProjectOptionsHandler.promises.setCompiler.should.have.been.calledWithMatch(
-          ctx.project_id,
-          ctx.compiler
-        )
-        ctx.ProjectOptionsHandler.promises.setImageName.should.have.been.calledWithMatch(
-          ctx.project_id,
-          ctx.imageName
-        )
+      it('should set project rootDoc when passed', function (ctx) {
         ctx.ProjectRootDocManager.promises.setRootDocFromName.should.have.been.calledWithMatch(
           ctx.project_id,
           ctx.mainFile
         )
-        ctx.ProjectOptionsHandler.promises.setBrandVariationId.should.have.been.calledWithMatch(
-          ctx.project_id,
-          ctx.brandVariationId
-        )
-      })
-
-      it('should update project', function (ctx) {
-        ctx.Project.updateOne.should.have.been.calledWithMatch(
-          { _id: ctx.project_id },
-          {
-            fromV1TemplateId: ctx.templateId,
-            fromV1TemplateVersionId: ctx.templateVersionId,
-          }
-        )
       })
     })
 
@@ -234,20 +217,24 @@ describe('TemplatesManager', function () {
         )
       })
 
-      it('should not set missing project options', function (ctx) {
-        ctx.ProjectOptionsHandler.promises.setCompiler.called.should.equal(
-          false
+      it('should create project', function (ctx) {
+        ctx.ProjectUploadManager.promises.createProjectFromZipArchiveWithName.should.have.been.calledWithMatch(
+          ctx.user_id,
+          ctx.templateName,
+          ctx.dumpPath,
+          {
+            fromV1TemplateId: ctx.templateId,
+            fromV1TemplateVersionId: ctx.templateVersionId,
+            compiler: 'pdflatex',
+            imageName: 'wl_texlive:2018.1',
+          }
         )
+      })
+
+      it('should not set missing project options', function (ctx) {
         ctx.ProjectRootDocManager.promises.setRootDocFromName.called.should.equal(
           false
         )
-        ctx.ProjectOptionsHandler.promises.setBrandVariationId.called.should.equal(
-          false
-        )
-        ctx.ProjectOptionsHandler.promises.setImageName.should.have.been.calledWithMatch(
-          ctx.project_id,
-          'wl_texlive:2018.1'
-        )
       })
     })
   })