forked from EduCraft/curriculum-project-hub
fix: import legacy projects without wrapper folder
This commit is contained in:
@@ -81,7 +81,7 @@ export async function importLegacyProjects(input: {
|
|||||||
sourceRelativePath: entry.sourceRelativePath,
|
sourceRelativePath: entry.sourceRelativePath,
|
||||||
};
|
};
|
||||||
await writeState(input.stateFile, { version: 1, projects });
|
await writeState(input.stateFile, { version: 1, projects });
|
||||||
const folderId = await ensureFolderPath(input.prisma, input.organizationId, ["旧教学资产", ...entry.folderPath]);
|
const folderId = await ensureFolderPath(input.prisma, input.organizationId, entry.folderPath);
|
||||||
input.onProgress?.(`import ${entry.legacyId}: ${entry.name}`);
|
input.onProgress?.(`import ${entry.legacyId}: ${entry.name}`);
|
||||||
const created = await createProjectFromOrgAdmin(input.prisma, {
|
const created = await createProjectFromOrgAdmin(input.prisma, {
|
||||||
organizationId: input.organizationId,
|
organizationId: input.organizationId,
|
||||||
@@ -123,7 +123,7 @@ async function ensureFolderPath(
|
|||||||
prisma: PrismaClient,
|
prisma: PrismaClient,
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
parts: readonly string[],
|
parts: readonly string[],
|
||||||
): Promise<string> {
|
): Promise<string | undefined> {
|
||||||
let parentId: string | undefined;
|
let parentId: string | undefined;
|
||||||
for (const name of parts) {
|
for (const name of parts) {
|
||||||
const existing = await prisma.folder.findFirst({
|
const existing = await prisma.folder.findFirst({
|
||||||
@@ -141,7 +141,6 @@ async function ensureFolderPath(
|
|||||||
});
|
});
|
||||||
parentId = created.id;
|
parentId = created.id;
|
||||||
}
|
}
|
||||||
if (parentId === undefined) throw new Error("legacy import folder path is empty");
|
|
||||||
return parentId;
|
return parentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ describe("legacy teaching-material project import", () => {
|
|||||||
|
|
||||||
afterAll(async () => prisma.$disconnect());
|
afterAll(async () => prisma.$disconnect());
|
||||||
|
|
||||||
it("imports each legacy project as an unbound resumable project under its old folder path", async () => {
|
it("imports each legacy project as an unbound resumable project under its original folder path", async () => {
|
||||||
const sourceRoot = await temporaryRoot("cph-legacy-source-");
|
const sourceRoot = await temporaryRoot("cph-legacy-source-");
|
||||||
const workspaceRoot = await temporaryRoot("cph-legacy-target-");
|
const workspaceRoot = await temporaryRoot("cph-legacy-target-");
|
||||||
const projectSource = join(sourceRoot, "物理", "M-243-牛顿力学");
|
const projectSource = join(sourceRoot, "物理", "M-243-牛顿力学");
|
||||||
@@ -70,7 +70,7 @@ describe("legacy teaching-material project import", () => {
|
|||||||
});
|
});
|
||||||
expect(project.name).toBe("牛顿力学");
|
expect(project.name).toBe("牛顿力学");
|
||||||
expect(project.folder?.name).toBe("物理");
|
expect(project.folder?.name).toBe("物理");
|
||||||
expect(project.folder?.parent?.name).toBe("旧教学资产");
|
expect(project.folder?.parent).toBeNull();
|
||||||
expect(project.groupBindings).toEqual([]);
|
expect(project.groupBindings).toEqual([]);
|
||||||
await expect(readFile(join(imported.workspaceDir, "chapters", "lesson.typ"), "utf8"))
|
await expect(readFile(join(imported.workspaceDir, "chapters", "lesson.typ"), "utf8"))
|
||||||
.resolves.toBe("= 牛顿第二定律\n");
|
.resolves.toBe("= 牛顿第二定律\n");
|
||||||
@@ -116,6 +116,36 @@ describe("legacy teaching-material project import", () => {
|
|||||||
await expect(prisma.project.count({ where: { id: imported.projectId } })).resolves.toBe(1);
|
await expect(prisma.project.count({ where: { id: imported.projectId } })).resolves.toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("places a legacy project without a source folder in the system Inbox", async () => {
|
||||||
|
const sourceRoot = await temporaryRoot("cph-legacy-source-");
|
||||||
|
const workspaceRoot = await temporaryRoot("cph-legacy-target-");
|
||||||
|
const projectSource = join(sourceRoot, "top-level-project");
|
||||||
|
await mkdir(join(projectSource, "workspace"), { recursive: true });
|
||||||
|
await writeFile(join(projectSource, "workspace", "project.toml"), "title = \"顶层项目\"\n");
|
||||||
|
await writeFile(join(projectSource, "project.json"), "{\"id\":\"top-level\"}\n");
|
||||||
|
|
||||||
|
const state = await importLegacyProjects({
|
||||||
|
prisma,
|
||||||
|
organizationId: DEFAULT_ORG_ID,
|
||||||
|
actorFeishuOpenId: "ou_legacy_owner",
|
||||||
|
workspaceRoot,
|
||||||
|
sourceRoot,
|
||||||
|
stateFile: join(workspaceRoot, "state.json"),
|
||||||
|
projects: [{
|
||||||
|
legacyId: "top-level",
|
||||||
|
name: "顶层项目",
|
||||||
|
folderPath: [],
|
||||||
|
sourceRelativePath: "top-level-project",
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
const project = await prisma.project.findUniqueOrThrow({
|
||||||
|
where: { id: state.projects["top-level"]?.projectId },
|
||||||
|
include: { folder: true },
|
||||||
|
});
|
||||||
|
expect(project.folder).toMatchObject({ name: "Inbox", kind: "SYSTEM_INBOX" });
|
||||||
|
});
|
||||||
|
|
||||||
it("rejects symlinks instead of importing paths outside the staged project", async () => {
|
it("rejects symlinks instead of importing paths outside the staged project", async () => {
|
||||||
const sourceRoot = await temporaryRoot("cph-legacy-symlink-");
|
const sourceRoot = await temporaryRoot("cph-legacy-symlink-");
|
||||||
const workspaceRoot = await temporaryRoot("cph-legacy-target-");
|
const workspaceRoot = await temporaryRoot("cph-legacy-target-");
|
||||||
|
|||||||
Reference in New Issue
Block a user