Migrate url tooling to twenty-shared (#10440)

Migrate and unify URL tooling in twenty-shared.

We now have:
- isValidHostname which follows our own business rules
- a zod schema that can be re-used in different context and leverages is
isValidHostname
- isValidUrl on top of the zod schema
- a getAbsoluteURl and getHostname on top of the zod schema

I have added a LOT of tests to cover all the cases I've found

Also fixes: https://github.com/twentyhq/twenty/issues/10147
This commit is contained in:
Charles Bochet
2025-02-24 18:01:51 +01:00
committed by GitHub
parent d4bdae562f
commit 9046a9ac16
28 changed files with 280 additions and 158 deletions

View File

@ -5,7 +5,7 @@ import { IconChevronRight } from 'twenty-ui';
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { getUrlHostname } from '~/utils/url/getUrlHostname';
import { getUrlHostnameOrThrow } from 'twenty-shared';
export const StyledApisFieldTableRow = styled(TableRow)`
grid-template-columns: 1fr 28px;
@ -39,7 +39,7 @@ export const SettingsDevelopersWebhookTableRow = ({
return (
<StyledApisFieldTableRow to={to}>
<StyledUrlTableCell>
{getUrlHostname(fieldItem.targetUrl, { keepPath: true })}
{getUrlHostnameOrThrow(fieldItem.targetUrl)}
</StyledUrlTableCell>
<StyledIconTableCell>
<StyledIconChevronRight

View File

@ -1,18 +1,16 @@
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useState } from 'react';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
import { useDebouncedCallback } from 'use-debounce';
import { WebhookOperationType } from '~/pages/settings/developers/webhooks/types/WebhookOperationsType';
import { isDefined } from 'twenty-shared';
import { WEBHOOK_EMPTY_OPERATION } from '~/pages/settings/developers/webhooks/constants/WebhookEmptyOperation';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
import { SettingsPath } from '@/types/SettingsPath';
import { useState } from 'react';
import { getUrlHostnameOrThrow, isDefined, isValidUrl } from 'twenty-shared';
import { useDebouncedCallback } from 'use-debounce';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { isValidUrl } from '~/utils/url/isValidUrl';
import { getUrlHostname } from '~/utils/url/getUrlHostname';
import { WEBHOOK_EMPTY_OPERATION } from '~/pages/settings/developers/webhooks/constants/WebhookEmptyOperation';
import { WebhookOperationType } from '~/pages/settings/developers/webhooks/types/WebhookOperationsType';
type WebhookFormData = {
targetUrl: string;
@ -106,9 +104,7 @@ export const useWebhookUpdateForm = ({
const isTargetUrlValid = isValidUrl(trimmedUrl);
setIsTargetUrlValid(isTargetUrlValid);
if (isTargetUrlValid) {
setTitle(
getUrlHostname(trimmedUrl, { keepPath: true }) || 'New Webhook',
);
setTitle(getUrlHostnameOrThrow(trimmedUrl) || 'New Webhook');
}
}
};
@ -177,9 +173,7 @@ export const useWebhookUpdateForm = ({
operations,
secret: data.secret,
});
setTitle(
getUrlHostname(data.targetUrl, { keepPath: true }) || 'New Webhook',
);
setTitle(getUrlHostnameOrThrow(data.targetUrl) || 'New Webhook');
setLoading(false);
},
});