Skip to content

Commit 61dd5f4

Browse files
authored
🐛(sync) OneDrive createDir recursion (#1390)
1 parent 9407557 commit 61dd5f4

2 files changed

Lines changed: 60 additions & 20 deletions

File tree

packages/filesystem/onedrive/onedrive.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,38 @@ describe("OneDriveFileSystem", () => {
9393

9494
await expect(fs.delete("missing.txt")).resolves.toBeUndefined();
9595
});
96+
97+
it("createDir should create nested directories from root", async () => {
98+
const fs = new OneDriveFileSystem("/", "token");
99+
const requestSpy = vi.spyOn(fs, "request").mockResolvedValue({});
100+
101+
await expect(fs.createDir("A/B/C")).resolves.toBeUndefined();
102+
103+
expect(requestSpy).toHaveBeenCalledTimes(3);
104+
expect(requestSpy.mock.calls[0][0]).toBe("https://graph.microsoft.com/v1.0/me/drive/special/approot/children");
105+
expect(requestSpy.mock.calls[1][0]).toBe("https://graph.microsoft.com/v1.0/me/drive/special/approot:/A:/children");
106+
expect(requestSpy.mock.calls[2][0]).toBe(
107+
"https://graph.microsoft.com/v1.0/me/drive/special/approot:/A/B:/children"
108+
);
109+
expect(JSON.parse((requestSpy.mock.calls[2][1] as RequestInit).body as string)).toMatchObject({
110+
name: "C",
111+
folder: {},
112+
"@microsoft.graph.conflictBehavior": "fail",
113+
});
114+
});
115+
116+
it("createDir should continue when an intermediate directory already exists", async () => {
117+
const fs = new OneDriveFileSystem("/", "token");
118+
const requestSpy = vi
119+
.spyOn(fs, "request")
120+
.mockRejectedValueOnce(new Error('{"error":{"code":"nameAlreadyExists"}}'))
121+
.mockResolvedValueOnce({});
122+
123+
await expect(fs.createDir("A/B")).resolves.toBeUndefined();
124+
125+
expect(requestSpy).toHaveBeenCalledTimes(2);
126+
expect(JSON.parse((requestSpy.mock.calls[1][1] as RequestInit).body as string)).toMatchObject({
127+
name: "B",
128+
});
129+
});
96130
});

packages/filesystem/onedrive/onedrive.ts

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,31 +45,37 @@ export default class OneDriveFileSystem implements FileSystem {
4545
if (!dir) {
4646
return;
4747
}
48-
dir = joinPath(this.path, dir);
49-
const dirs = dir.split("/");
50-
let parent = "";
51-
if (dirs.length > 2) {
52-
parent = dirs.slice(0, dirs.length - 1).join("/");
53-
}
48+
const dirs = joinPath(this.path, dir).split("/").filter(Boolean);
5449
const myHeaders = new Headers();
5550
myHeaders.append("Content-Type", "application/json");
56-
if (parent !== "") {
57-
parent = `:${parent}:`;
58-
}
59-
const data = await this.request(`https://graph.microsoft.com/v1.0/me/drive/special/approot${parent}/children`, {
60-
method: "POST",
61-
headers: myHeaders,
62-
body: JSON.stringify({
63-
name: dirs[dirs.length - 1],
64-
folder: {},
65-
"@microsoft.graph.conflictBehavior": "replace",
66-
}),
67-
});
68-
if (data.errno) {
69-
throw new Error(JSON.stringify(data));
51+
52+
for (let i = 0; i < dirs.length; i++) {
53+
const parentPath = dirs.slice(0, i).join("/");
54+
const parent = parentPath ? `:/${parentPath}:` : "";
55+
try {
56+
await this.request(`https://graph.microsoft.com/v1.0/me/drive/special/approot${parent}/children`, {
57+
method: "POST",
58+
headers: myHeaders,
59+
body: JSON.stringify({
60+
name: dirs[i],
61+
folder: {},
62+
"@microsoft.graph.conflictBehavior": "fail",
63+
}),
64+
});
65+
} catch (error) {
66+
if (this.isDirectoryAlreadyExistsError(error)) {
67+
continue;
68+
}
69+
throw error;
70+
}
7071
}
7172
}
7273

74+
private isDirectoryAlreadyExistsError(error: unknown): boolean {
75+
const msg = String(error);
76+
return msg.includes("nameAlreadyExists") || msg.includes("itemAlreadyExists");
77+
}
78+
7379
request(url: string, config?: RequestInit, nothen?: boolean): Promise<Response | any> {
7480
config = config || {};
7581
const headers = <Headers>config.headers || new Headers();

0 commit comments

Comments
 (0)