fix: simplify normalizeUrlOrigin to use standard URL API and fix lint

Use URL() properties directly instead of manual string parsing.
Remove real customer name from test data. Fix duplicate imports and
prettier formatting.
This commit is contained in:
Charles Bochet
2026-03-20 16:21:11 +01:00
parent 53fb2bef12
commit 8c5f256f49
6 changed files with 20 additions and 34 deletions
@@ -1,12 +1,11 @@
import { NavigationMenuItemType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { isDefined, normalizeUrl } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import type { NavigationMenuItem } from '~/generated-metadata/graphql';
import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorLink';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { computeInsertIndexAndPosition } from '@/navigation-menu-item/common/utils/computeInsertIndexAndPosition';
import { normalizeUrl } from 'twenty-shared/utils';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
export const useAddLinkToNavigationMenuDraft = () => {
@@ -104,9 +104,7 @@ const getUniqueValues = (
.primaryLinkUrl,
)
) {
return normalizeUrlOrigin(
row?.[columnName]?.toString().trim() || '',
);
return normalizeUrlOrigin(row?.[columnName]?.toString().trim() || '');
}
return row?.[columnName]?.toString().trim().toLowerCase();
@@ -1,11 +1,7 @@
import { isNonEmptyString } from '@sniptt/guards';
import isEmpty from 'lodash.isempty';
import { type LinkMetadataNullable } from 'twenty-shared/types';
import {
isDefined,
normalizeUrlOrigin,
parseJson,
} from 'twenty-shared/utils';
import { isDefined, normalizeUrlOrigin, parseJson } from 'twenty-shared/utils';
import { removeEmptyLinks } from 'src/engine/core-modules/record-transformer/utils/remove-empty-links';
@@ -44,9 +40,7 @@ export const transformLinksValue = (
const processedSecondaryLinks = secondaryLinks?.map((link) => ({
...link,
url: isDefined(link.url)
? normalizeUrlOrigin(link.url)
: link.url,
url: isDefined(link.url) ? normalizeUrlOrigin(link.url) : link.url,
}));
return {
@@ -7,10 +7,7 @@ import {
type ConnectedAccountProvider,
type FieldActorSource,
} from 'twenty-shared/types';
import {
isDefined,
normalizeUrlOrigin,
} from 'twenty-shared/utils';
import { isDefined, normalizeUrlOrigin } from 'twenty-shared/utils';
import { type DeepPartial, ILike } from 'typeorm';
import { SecureHttpClientService } from 'src/engine/core-modules/secure-http-client/secure-http-client.service';
@@ -39,19 +39,19 @@ describe('normalizeUrlOrigin', () => {
expected: 'https://www.example.com/TEST#Hash',
},
{
title: 'should preserve special characters in path',
input: 'https://test.test/edouard-ménard-22219837',
expected: 'https://test.test/edouard-ménard-22219837',
title: 'should percent-encode non-ASCII characters in path',
input: 'https://test.test/john-döe-22219837',
expected: 'https://test.test/john-d%C3%B6e-22219837',
},
{
title: 'should preserve already encoded special characters in path',
input: 'https://test.test/edouard-m%C3%A9nard-22219837',
expected: 'https://test.test/edouard-m%C3%A9nard-22219837',
input: 'https://test.test/john-d%C3%B6e-22219837',
expected: 'https://test.test/john-d%C3%B6e-22219837',
},
{
title: 'should preserve special characters in query params',
title: 'should percent-encode non-ASCII characters in query params',
input: 'https://example.com/path?name=José',
expected: 'https://example.com/path?name=José',
expected: 'https://example.com/path?name=Jos%C3%A9',
},
{
title:
@@ -66,9 +66,9 @@ describe('normalizeUrlOrigin', () => {
expected: 'https://example.com/test%2520name',
},
{
title: 'should preserve special characters in hash fragments',
title: 'should percent-encode non-ASCII characters in hash fragments',
input: 'https://example.com/path#frédéric',
expected: 'https://example.com/path#frédéric',
expected: 'https://example.com/path#fr%C3%A9d%C3%A9ric',
},
{
title: 'should keep encoded characters in hash fragments as-is',
@@ -2,7 +2,8 @@ import { getURLSafely } from '@/utils/getURLSafely';
import { isDefined } from '@/utils/validation';
// Lowercases the URL origin (scheme + host) and removes a trailing slash.
// Preserves the raw path, query, and hash without decoding percent-encoded sequences.
// URL() already lowercases the origin and preserves percent-encoded sequences
// in the path, query, and hash (e.g. %2F stays %2F, %2520 stays %2520).
export const normalizeUrlOrigin = (rawUrl: string) => {
const url = getURLSafely(rawUrl);
@@ -10,11 +11,8 @@ export const normalizeUrlOrigin = (rawUrl: string) => {
return rawUrl;
}
const lowercaseOrigin = url.origin.toLowerCase();
const rawOrigin = rawUrl.match(/^[a-zA-Z][a-zA-Z\d+.-]*:\/\/[^/?#]+/)?.[0];
const path = isDefined(rawOrigin)
? rawUrl.slice(rawOrigin.length)
: url.pathname + url.search + url.hash;
return (lowercaseOrigin + path).replace(/\/$/, '');
return (url.origin + url.pathname + url.search + url.hash).replace(
/\/$/,
'',
);
};