Repository: perry-mitchell/webdav-client
Pull request #343 (2023-05-25 08:06:43)

feat(client): allow custom directory base path
To solve #342


Patch:
diff --git a/source/factory.ts b/source/factory.ts
index 6f7e28a..a3c27f7 100644
--- a/source/factory.ts
+++ b/source/factory.ts
@@ -48,7 +48,8 @@ export function createClient(remoteURL: string, options: WebDAVClientOptions = {
         password,
         token,
         username,
-        withCredentials
+        withCredentials,
+        directoryBasePath
     } = options;
     let authType = authTypeRaw;
     if (!authType) {
@@ -66,7 +67,8 @@ export function createClient(remoteURL: string, options: WebDAVClientOptions = {
         password,
         token,
         username,
-        withCredentials
+        withCredentials,
+        directoryBasePath
     };
     setupAuth(context, username, password, token, ha1);
     return {
diff --git a/source/operations/directoryContents.ts b/source/operations/directoryContents.ts
index 1933e9d..bd79229 100644
--- a/source/operations/directoryContents.ts
+++ b/source/operations/directoryContents.ts
@@ -1,6 +1,6 @@
 import pathPosix from "path-posix";
 import { joinURL, normaliseHREF } from "../tools/url.js";
-import { encodePath, normalisePath } from "../tools/path.js";
+import { encodePath, makePathAbsolute, normalisePath } from "../tools/path.js";
 import { parseXML, prepareFileFromProps } from "../tools/dav.js";
 import { request, prepareRequestOptions } from "../request.js";
 import { handleResponseCode, processGlobFilter, processResponsePayload } from "../response.js";
@@ -36,10 +36,11 @@ export async function getDirectoryContents(
         throw new Error("Failed parsing directory contents: Empty response");
     }
     const davResp = await parseXML(responseData);
-    const _remotePath = remotePath.startsWith("/") ? remotePath : "/" + remotePath;
+    const _remotePath = makePathAbsolute(remotePath);
+    const directoryBasePath = makePathAbsolute(context.directoryBasePath || context.remotePath);
     let files = getDirectoryFiles(
         davResp,
-        context.remotePath,
+        directoryBasePath,
         _remotePath,
         options.details,
         options.includeSelf
diff --git a/source/tools/path.ts b/source/tools/path.ts
index 2aaa501..9abcb82 100644
--- a/source/tools/path.ts
+++ b/source/tools/path.ts
@@ -35,3 +35,7 @@ export function normalisePath(pathStr: string): string {
     }
     return normalisedPath;
 }
+
+export function makePathAbsolute(pathStr: string): string {
+    return pathStr.startsWith("/") ? pathStr : "/" + pathStr;
+}
diff --git a/source/types.ts b/source/types.ts
index 973df34..e3a24b9 100644
--- a/source/types.ts
+++ b/source/types.ts
@@ -260,6 +260,7 @@ export interface WebDAVClientContext {
     token?: OAuthToken;
     username?: string;
     withCredentials?: boolean;
+    directoryBasePath?: string;
 }
 
 export interface WebDAVClientError extends Error {
@@ -280,6 +281,7 @@ export interface WebDAVClientOptions {
     token?: OAuthToken;
     username?: string;
     withCredentials?: boolean;
+    directoryBasePath?: string;
 }
 
 export interface WebDAVMethodOptions {


Test patch:
diff --git a/test/node/operations/getDirectoryContents.spec.ts b/test/node/operations/getDirectoryContents.spec.ts
index 39baed6..48f7717 100644
--- a/test/node/operations/getDirectoryContents.spec.ts
+++ b/test/node/operations/getDirectoryContents.spec.ts
@@ -288,4 +288,21 @@ describe("getDirectoryContents", function () {
                 });
         });
     });
+
+    describe("when using custom dir base path", function () {
+        beforeEach(async function () {
+            this.client = createWebDAVClient(`http://localhost:${SERVER_PORT}/webdav/server`, {
+                username: SERVER_USERNAME,
+                password: SERVER_PASSWORD,
+                directoryBasePath: "/webdav/server/custom"
+            });
+        });
+
+        it("return correct filename with custom path", function () {
+            return this.client.getDirectoryContents("/").then(function (contents: Array<FileStat>) {
+                const file = contents.find(item => item.basename === "notes.txt");
+                expect(file.filename).to.equal("/../notes.txt");
+            });
+        });
+    });
 });