mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
fix: Duplicate paths in export ZIP (#12674)
This commit is contained in:
@@ -150,26 +150,12 @@ export default abstract class ExportDocumentTreeTask extends ExportTask {
|
||||
includeAttachments = true
|
||||
) {
|
||||
const pathMap = this.createPathMap(collections, format);
|
||||
Logger.debug(
|
||||
"task",
|
||||
`Start adding ${Object.values(pathMap).length} documents to archive`
|
||||
);
|
||||
|
||||
for (const path of pathMap) {
|
||||
const documentId = path[0].replace("/doc/", "");
|
||||
const pathInZip = path[1];
|
||||
|
||||
await this.processDocument({
|
||||
await this.addDocumentsToArchive({
|
||||
zip,
|
||||
pathInZip,
|
||||
documentId,
|
||||
includeAttachments,
|
||||
format,
|
||||
pathMap,
|
||||
format,
|
||||
includeAttachments,
|
||||
});
|
||||
}
|
||||
|
||||
Logger.debug("task", "Completed adding documents to archive");
|
||||
|
||||
return await ZipHelper.toTmpFile(zip);
|
||||
}
|
||||
@@ -200,28 +186,58 @@ export default abstract class ExportDocumentTreeTask extends ExportTask {
|
||||
format
|
||||
);
|
||||
|
||||
Logger.debug(
|
||||
"task",
|
||||
`Start adding ${Object.values(pathMap).length} documents to archive`
|
||||
);
|
||||
await this.addDocumentsToArchive({
|
||||
zip,
|
||||
pathMap,
|
||||
format,
|
||||
includeAttachments: true,
|
||||
});
|
||||
|
||||
for (const entry of pathMap) {
|
||||
const documentId = entry[0].replace("/doc/", "");
|
||||
const pathInZip = entry[1];
|
||||
return await ZipHelper.toTmpFile(zip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes each unique document in the path map and adds it to the zip.
|
||||
*
|
||||
* @param zip The yazl ZipFile to add files to
|
||||
* @param pathMap Map of document urls to their path in the zip
|
||||
* @param format The format to export in
|
||||
* @param includeAttachments Whether to include attachments in the export
|
||||
*/
|
||||
private async addDocumentsToArchive({
|
||||
zip,
|
||||
pathMap,
|
||||
format,
|
||||
includeAttachments,
|
||||
}: {
|
||||
zip: ZipFile;
|
||||
pathMap: Map<string, string>;
|
||||
format: FileOperationFormat;
|
||||
includeAttachments: boolean;
|
||||
}) {
|
||||
const processedPaths = new Set<string>();
|
||||
|
||||
Logger.debug("task", `Start adding documents to archive`);
|
||||
|
||||
for (const [url, pathInZip] of pathMap) {
|
||||
// A document may be keyed by multiple urls in the path map, only
|
||||
// process each file in the zip once.
|
||||
if (processedPaths.has(pathInZip)) {
|
||||
continue;
|
||||
}
|
||||
processedPaths.add(pathInZip);
|
||||
|
||||
await this.processDocument({
|
||||
zip,
|
||||
pathInZip,
|
||||
documentId,
|
||||
includeAttachments: true,
|
||||
documentId: url.replace("/doc/", ""),
|
||||
includeAttachments,
|
||||
format,
|
||||
pathMap,
|
||||
});
|
||||
}
|
||||
|
||||
Logger.debug("task", "Completed adding documents to archive");
|
||||
|
||||
return await ZipHelper.toTmpFile(zip);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import fs from "fs-extra";
|
||||
import ZipHelper from "@server/utils/ZipHelper";
|
||||
import {
|
||||
buildCollection,
|
||||
buildDocument,
|
||||
buildFileOperation,
|
||||
buildTeam,
|
||||
buildUser,
|
||||
} from "@server/test/factories";
|
||||
import ExportMarkdownZipTask from "./ExportMarkdownZipTask";
|
||||
|
||||
describe("ExportMarkdownZipTask", () => {
|
||||
it("should not duplicate documents in the zip file", async () => {
|
||||
const team = await buildTeam();
|
||||
const user = await buildUser({ teamId: team.id });
|
||||
const collection = await buildCollection({
|
||||
teamId: team.id,
|
||||
createdById: user.id,
|
||||
});
|
||||
const documents = await Promise.all([
|
||||
buildDocument({
|
||||
teamId: team.id,
|
||||
userId: user.id,
|
||||
collectionId: collection.id,
|
||||
title: "Test1",
|
||||
}),
|
||||
buildDocument({
|
||||
teamId: team.id,
|
||||
userId: user.id,
|
||||
collectionId: collection.id,
|
||||
title: "Test2",
|
||||
}),
|
||||
]);
|
||||
for (const document of documents) {
|
||||
await collection.addDocumentToStructure(document);
|
||||
}
|
||||
const fileOperation = await buildFileOperation({
|
||||
teamId: team.id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
const task = new ExportMarkdownZipTask();
|
||||
const filePath = await task.exportCollections([collection], fileOperation);
|
||||
|
||||
try {
|
||||
const fileNames: string[] = [];
|
||||
await ZipHelper.walk(filePath, (entry) => {
|
||||
if (!entry.isDirectory) {
|
||||
fileNames.push(entry.fileName);
|
||||
}
|
||||
});
|
||||
|
||||
expect(fileNames.sort()).toEqual([
|
||||
`${collection.name}/Test1.md`,
|
||||
`${collection.name}/Test2.md`,
|
||||
]);
|
||||
} finally {
|
||||
await fs.remove(filePath);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user