Skip to content

Commit a9ebb57

Browse files
authored
Merge pull request #302 from internxt/fix/webdav-not-found-errors
[_]: fix/webdav-not-found-errors
2 parents 6dffcf6 + d204da6 commit a9ebb57

15 files changed

Lines changed: 145 additions & 198 deletions

src/utils/webdav.utils.ts

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import { WebDavRequestedResource } from '../types/webdav.types';
44
import { DriveFolderService } from '../services/drive/drive-folder.service';
55
import { DriveFileService } from '../services/drive/drive-file.service';
66
import { DriveFileItem, DriveFolderItem } from '../types/drive.types';
7-
import { ConflictError, NotFoundError } from './errors.utils';
8-
import AppError from '@internxt/sdk/dist/shared/types/errors';
97

108
export class WebDavUtils {
119
static joinURL(...pathComponents: string[]): string {
@@ -59,51 +57,27 @@ export class WebDavUtils {
5957
}
6058
}
6159

62-
static async getDriveItemFromResource(
63-
resource: WebDavRequestedResource,
64-
driveFolderService?: DriveFolderService,
65-
driveFileService?: DriveFileService,
66-
): Promise<DriveFileItem | DriveFolderItem | undefined> {
67-
let item: DriveFileItem | DriveFolderItem | undefined = undefined;
68-
69-
if (resource.type === 'folder') {
70-
// if resource has a parentPath it means it's a subfolder then try to get it; if it throws an error it means it doesn't
71-
// exist and we should throw a 409 error in compliance with the WebDAV RFC
72-
// catch the error during getting parent folder and throw a 409 error in compliance with the WebDAV RFC
73-
try {
74-
item = await driveFolderService?.getFolderMetadataByPath(resource.url);
75-
} catch (error) {
76-
// if the error is a 404 error, it means the resource doesn't exist
77-
// in this case, throw a 409 error in compliance with the WebDAV RFC
78-
if ((error as AppError).status === 404) {
79-
throw new ConflictError(`Resource not found on Internxt Drive at ${resource.url}`);
80-
}
81-
throw error;
82-
}
83-
}
84-
if (resource.type === 'file') {
85-
try {
86-
item = await driveFileService?.getFileMetadataByPath(resource.url);
87-
} catch {
88-
//no op
89-
}
90-
}
91-
return item;
92-
}
93-
94-
static async getAndSearchItemFromResource({
60+
static async getDriveItemFromResource({
9561
resource,
9662
driveFolderService,
9763
driveFileService,
9864
}: {
9965
resource: WebDavRequestedResource;
10066
driveFolderService?: DriveFolderService;
10167
driveFileService?: DriveFileService;
102-
}): Promise<DriveFileItem | DriveFolderItem> {
103-
const driveItem = await this.getDriveItemFromResource(resource, driveFolderService, driveFileService);
104-
if (!driveItem) {
105-
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
68+
}): Promise<DriveFileItem | DriveFolderItem | undefined> {
69+
let item: DriveFileItem | DriveFolderItem | undefined = undefined;
70+
71+
try {
72+
if (resource.type === 'folder') {
73+
item = await driveFolderService?.getFolderMetadataByPath(resource.url);
74+
}
75+
if (resource.type === 'file') {
76+
item = await driveFileService?.getFileMetadataByPath(resource.url);
77+
}
78+
} catch {
79+
//no op
10680
}
107-
return driveItem;
81+
return item;
10882
}
10983
}

src/webdav/handlers/DELETE.handler.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { TrashService } from '../../services/drive/trash.service';
55
import { webdavLogger } from '../../utils/logger.utils';
66
import { DriveFileService } from '../../services/drive/drive-file.service';
77
import { DriveFolderService } from '../../services/drive/drive-folder.service';
8+
import { NotFoundError } from '../../utils/errors.utils';
89

910
export class DELETERequestHandler implements WebDavMethodHandler {
1011
constructor(
@@ -20,12 +21,16 @@ export class DELETERequestHandler implements WebDavMethodHandler {
2021
const resource = await WebDavUtils.getRequestedResource(req);
2122
webdavLogger.info(`[DELETE] Request received for ${resource.type} at ${resource.url}`);
2223

23-
const driveItem = await WebDavUtils.getAndSearchItemFromResource({
24+
const driveItem = await WebDavUtils.getDriveItemFromResource({
2425
resource,
2526
driveFolderService,
2627
driveFileService: driveFileService,
2728
});
2829

30+
if (!driveItem) {
31+
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
32+
}
33+
2934
webdavLogger.info(`[DELETE] [${driveItem.uuid}] Trashing ${resource.type}`);
3035
await trashService.trashItems({
3136
items: [{ type: resource.type, uuid: driveItem.uuid }],

src/webdav/handlers/GET.handler.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ export class GETRequestHandler implements WebDavMethodHandler {
3030
if (resource.type === 'folder') throw new NotFoundError('Folders cannot be listed with GET. Use PROPFIND instead.');
3131

3232
webdavLogger.info(`[GET] Request received for ${resource.type} at ${resource.url}`);
33-
const driveFile = (await WebDavUtils.getAndSearchItemFromResource({
33+
const driveItem = await WebDavUtils.getDriveItemFromResource({
3434
resource,
3535
driveFileService,
36-
})) as DriveFileItem;
36+
});
37+
38+
if (!driveItem) {
39+
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
40+
}
41+
const driveFile = driveItem as DriveFileItem;
3742

3843
webdavLogger.info(`[GET] [${driveFile.uuid}] Found Drive File`);
3944

src/webdav/handlers/HEAD.handler.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { webdavLogger } from '../../utils/logger.utils';
55
import { DriveFileService } from '../../services/drive/drive-file.service';
66
import { DriveFileItem } from '../../types/drive.types';
77
import { NetworkUtils } from '../../utils/network.utils';
8+
import { NotFoundError } from '../../utils/errors.utils';
89

910
export class HEADRequestHandler implements WebDavMethodHandler {
1011
constructor(
@@ -25,10 +26,15 @@ export class HEADRequestHandler implements WebDavMethodHandler {
2526
webdavLogger.info(`[HEAD] Request received for ${resource.type} at ${resource.url}`);
2627

2728
try {
28-
const driveFile = (await WebDavUtils.getAndSearchItemFromResource({
29+
const driveItem = await WebDavUtils.getDriveItemFromResource({
2930
resource,
3031
driveFileService,
31-
})) as DriveFileItem;
32+
});
33+
34+
if (!driveItem) {
35+
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
36+
}
37+
const driveFile = driveItem as DriveFileItem;
3238

3339
webdavLogger.info(`[HEAD] [${driveFile.uuid}] Found Drive File`);
3440

src/webdav/handlers/MKCOL.handler.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { webdavLogger } from '../../utils/logger.utils';
66
import { XMLUtils } from '../../utils/xml.utils';
77
import { AsyncUtils } from '../../utils/async.utils';
88
import { DriveFolderItem } from '../../types/drive.types';
9-
import { MethodNotAllowed } from '../../utils/errors.utils';
9+
import { ConflictError, MethodNotAllowed } from '../../utils/errors.utils';
1010

1111
export class MKCOLRequestHandler implements WebDavMethodHandler {
1212
constructor(
@@ -23,19 +23,26 @@ export class MKCOLRequestHandler implements WebDavMethodHandler {
2323

2424
const parentResource = await WebDavUtils.getRequestedResource(resource.parentPath, false);
2525

26-
const parentFolderItem = (await WebDavUtils.getAndSearchItemFromResource({
26+
const parentDriveItem = await WebDavUtils.getDriveItemFromResource({
2727
resource: parentResource,
2828
driveFolderService,
29-
})) as DriveFolderItem;
30-
31-
let folderAlreadyExists = true;
32-
// try to get the folder from the drive before creating it
33-
// The method getFolderMetadataByPath will throw an error if the folder does not exist, so we need to catch it
34-
try {
35-
await driveFolderService.getFolderMetadataByPath(resource.url);
36-
} catch {
37-
folderAlreadyExists = false;
29+
});
30+
31+
if (!parentDriveItem) {
32+
// WebDAV RFC
33+
// When the MKCOL operation creates a new collection resource,
34+
// all ancestors MUST already exist, or the method MUST fail
35+
// with a 409 (Conflict) status code
36+
throw new ConflictError(`Parent folders not found on Internxt Drive at ${resource.url}`);
3837
}
38+
const parentFolderItem = parentDriveItem as DriveFolderItem;
39+
40+
const driveFolderItem = await WebDavUtils.getDriveItemFromResource({
41+
resource,
42+
driveFolderService,
43+
});
44+
45+
const folderAlreadyExists = !!driveFolderItem;
3946

4047
if (folderAlreadyExists) {
4148
webdavLogger.info(`[MKCOL] ❌ Folder '${resource.url}' already exists`);

src/webdav/handlers/MOVE.handler.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@ export class MOVERequestHandler implements WebDavMethodHandler {
3030

3131
webdavLogger.info('[MOVE] Destination resource found', { destinationResource });
3232

33-
const originalDriveItem = await WebDavUtils.getAndSearchItemFromResource({
33+
const originalDriveItem = await WebDavUtils.getDriveItemFromResource({
3434
resource,
3535
driveFolderService,
3636
driveFileService,
3737
});
3838

39+
if (!originalDriveItem) {
40+
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
41+
}
42+
3943
if (destinationResource.path.dir === resource.path.dir) {
4044
// RENAME (the operation is from the same dir)
4145
webdavLogger.info(
@@ -62,10 +66,15 @@ export class MOVERequestHandler implements WebDavMethodHandler {
6266
webdavLogger.info(`[MOVE] Moving ${resource.type} with UUID ${originalDriveItem.uuid} to ${destinationPath}`);
6367
const destinationFolderResource = await WebDavUtils.getRequestedResource(destinationResource.parentPath);
6468

65-
const destinationFolderItem = (await WebDavUtils.getAndSearchItemFromResource({
69+
const destinationDriveFolderItem = await WebDavUtils.getDriveItemFromResource({
6670
resource: destinationFolderResource,
6771
driveFolderService,
68-
})) as DriveFolderItem;
72+
});
73+
74+
if (!destinationDriveFolderItem) {
75+
throw new NotFoundError(`Resource not found on Internxt Drive at ${resource.url}`);
76+
}
77+
const destinationFolderItem = destinationDriveFolderItem as DriveFileItem;
6978

7079
if (resource.type === 'folder') {
7180
const folder = originalDriveItem as DriveFolderItem;

src/webdav/handlers/PROPFIND.handler.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,13 @@ export class PROPFINDRequestHandler implements WebDavMethodHandler {
2525
const resource = await WebDavUtils.getRequestedResource(req);
2626
webdavLogger.info(`[PROPFIND] Request received for ${resource.type} at ${resource.url}`);
2727

28-
try {
29-
const driveItem = await WebDavUtils.getAndSearchItemFromResource({
30-
resource,
31-
driveFolderService,
32-
driveFileService,
33-
});
34-
35-
switch (resource.type) {
36-
case 'file': {
37-
const fileMetaXML = await this.getFileMetaXML(resource, driveItem as DriveFileItem);
38-
res.status(207).send(fileMetaXML);
39-
break;
40-
}
28+
const driveItem = await WebDavUtils.getDriveItemFromResource({
29+
resource,
30+
driveFolderService,
31+
driveFileService,
32+
});
4133

42-
case 'folder': {
43-
const depth = req.header('depth') ?? '1';
44-
const folderMetaXML = await this.getFolderContentXML(resource, driveItem as DriveFolderItem, depth);
45-
res.status(207).send(folderMetaXML);
46-
break;
47-
}
48-
}
49-
} catch {
34+
if (!driveItem) {
5035
res.status(207).send(
5136
XMLUtils.toWebDavXML(
5237
{
@@ -64,6 +49,22 @@ export class PROPFINDRequestHandler implements WebDavMethodHandler {
6449
},
6550
),
6651
);
52+
return;
53+
}
54+
55+
switch (resource.type) {
56+
case 'file': {
57+
const fileMetaXML = await this.getFileMetaXML(resource, driveItem as DriveFileItem);
58+
res.status(207).send(fileMetaXML);
59+
break;
60+
}
61+
62+
case 'folder': {
63+
const depth = req.header('depth') ?? '1';
64+
const folderMetaXML = await this.getFolderContentXML(resource, driveItem as DriveFolderItem, depth);
65+
res.status(207).send(folderMetaXML);
66+
break;
67+
}
6768
}
6869
};
6970

src/webdav/handlers/PUT.handler.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { DriveFileService } from '../../services/drive/drive-file.service';
33
import { NetworkFacade } from '../../services/network/network-facade.service';
44
import { AuthService } from '../../services/auth.service';
55
import { WebDavMethodHandler } from '../../types/webdav.types';
6-
import { NotFoundError, UnsupportedMediaTypeError } from '../../utils/errors.utils';
6+
import { ConflictError, NotFoundError, UnsupportedMediaTypeError } from '../../utils/errors.utils';
77
import { WebDavUtils } from '../../utils/webdav.utils';
88
import { webdavLogger } from '../../utils/logger.utils';
9-
import { DriveFileItem, DriveFolderItem } from '../../types/drive.types';
9+
import { DriveFileItem } from '../../types/drive.types';
1010
import { DriveFolderService } from '../../services/drive/drive-folder.service';
1111
import { TrashService } from '../../services/drive/trash.service';
1212
import { EncryptionVersion } from '@internxt/sdk/dist/drive/storage/types';
@@ -43,15 +43,24 @@ export class PUTRequestHandler implements WebDavMethodHandler {
4343

4444
const parentResource = await WebDavUtils.getRequestedResource(resource.parentPath, false);
4545

46-
const parentFolderItem = (await WebDavUtils.getAndSearchItemFromResource({
46+
const parentDriveFolderItem = await WebDavUtils.getDriveItemFromResource({
4747
resource: parentResource,
4848
driveFolderService,
49-
})) as DriveFolderItem;
49+
});
50+
51+
if (!parentDriveFolderItem) {
52+
// WebDAV RFC
53+
// When the PUT operation creates a new resource,
54+
// all ancestors MUST already exist, or the method MUST fail
55+
// with a 409 (Conflict) status code
56+
throw new ConflictError(`Parent folders not found on Internxt Drive at ${resource.url}`);
57+
}
58+
const parentFolderItem = parentDriveFolderItem as DriveFileItem;
5059

5160
try {
5261
// If the file already exists, the WebDAV specification states that 'PUT /…/file' should replace it.
5362
// http://www.webdav.org/specs/rfc4918.html#put-resources
54-
const driveFileItem = (await WebDavUtils.getAndSearchItemFromResource({
63+
const driveFileItem = (await WebDavUtils.getDriveItemFromResource({
5564
resource: resource,
5665
driveFileService,
5766
})) as DriveFileItem;

0 commit comments

Comments
 (0)