From 099897013c4ea014212495965d4972e5078ed406 Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Thu, 18 Apr 2024 11:18:03 +0200 Subject: [PATCH] fix: Make `getParentContainer` work with query parameters --- .../identifiers/BaseIdentifierStrategy.ts | 21 +++++++++++++------ .../BaseIdentifierStrategy.test.ts | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/util/identifiers/BaseIdentifierStrategy.ts b/src/util/identifiers/BaseIdentifierStrategy.ts index b466198a8..db752ccbe 100644 --- a/src/util/identifiers/BaseIdentifierStrategy.ts +++ b/src/util/identifiers/BaseIdentifierStrategy.ts @@ -1,9 +1,19 @@ import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier'; import { errorTermsToMetadata } from '../errors/HttpErrorUtil'; import { InternalServerError } from '../errors/InternalServerError'; -import { ensureTrailingSlash, isContainerIdentifier } from '../PathUtil'; +import { isContainerIdentifier } from '../PathUtil'; import type { IdentifierStrategy } from './IdentifierStrategy'; +/** + * Regular expression used to determine the parent container of a resource. + */ +const parentRegex = /^(.+\/)[^/]+\/*$/u; + +/** + * Used during containment check to determine if an identifier is a direct child or not. + */ +const tailRegex = /\/./u; + /** * Provides a default implementation for `getParentContainer` * which checks if the identifier is supported and not a root container. @@ -26,10 +36,9 @@ export abstract class BaseIdentifierStrategy implements IdentifierStrategy { throw new InternalServerError(`Cannot obtain the parent of ${identifier.path} because it is a root container.`); } - // Trailing slash is necessary for URL library - const parentPath = new URL('..', ensureTrailingSlash(identifier.path)).href; - - return { path: parentPath }; + // Due to the checks above we know this will always succeed + const match = parentRegex.exec(identifier.path); + return { path: match![1] }; } public abstract isRootContainer(identifier: ResourceIdentifier): boolean; @@ -49,6 +58,6 @@ export abstract class BaseIdentifierStrategy implements IdentifierStrategy { const tail = identifier.path.slice(container.path.length); // If there is at least one `/` followed by a char this is not a direct parent container - return !/\/./u.test(tail); + return !tailRegex.test(tail); } } diff --git a/test/unit/util/identifiers/BaseIdentifierStrategy.test.ts b/test/unit/util/identifiers/BaseIdentifierStrategy.test.ts index 3717522c4..a521c9976 100644 --- a/test/unit/util/identifiers/BaseIdentifierStrategy.test.ts +++ b/test/unit/util/identifiers/BaseIdentifierStrategy.test.ts @@ -19,7 +19,9 @@ describe('A BaseIdentifierStrategy', (): void => { describe('getParentContainer', (): void => { it('returns the parent identifier.', async(): Promise => { expect(strategy.getParentContainer({ path: 'http://example.com/foo/bar' })).toEqual({ path: 'http://example.com/foo/' }); + expect(strategy.getParentContainer({ path: 'http://example.com/foo//' })).toEqual({ path: 'http://example.com/' }); expect(strategy.getParentContainer({ path: 'http://example.com/foo/bar/' })).toEqual({ path: 'http://example.com/foo/' }); + expect(strategy.getParentContainer({ path: 'http://example.com/foo/bar?q=5' })).toEqual({ path: 'http://example.com/foo/' }); }); it('errors when attempting to get the parent of an unsupported identifier.', async(): Promise => {