fix: prevent saving API key with empty name (#18970)
Fixes: #18959 Generally, users need to type name before they create the api key. https://github.com/user-attachments/assets/bb3ec0ec-d05f-48a8-b762-a35288a9e111 <img width="656" height="559" alt="image" src="https://github.com/user-attachments/assets/cb8fd461-98f0-4475-b3f0-0de1e62624e2" />
This commit is contained in:
+6
-2
@@ -4,9 +4,10 @@ import {
|
|||||||
} from '@/settings/developers/utils/formatExpiration';
|
} from '@/settings/developers/utils/formatExpiration';
|
||||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
import { IconChevronRight } from 'twenty-ui/display';
|
|
||||||
import { styled } from '@linaria/react';
|
import { styled } from '@linaria/react';
|
||||||
|
import { useLingui } from '@lingui/react/macro';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
import { IconChevronRight } from 'twenty-ui/display';
|
||||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||||
import { type ApiKey } from '~/generated-metadata/graphql';
|
import { type ApiKey } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ export const SettingsApiKeysFieldItemTableRow = ({
|
|||||||
apiKey,
|
apiKey,
|
||||||
to,
|
to,
|
||||||
}: SettingsApiKeysFieldItemTableRowProps) => {
|
}: SettingsApiKeysFieldItemTableRowProps) => {
|
||||||
|
const { t } = useLingui();
|
||||||
const { theme } = useContext(ThemeContext);
|
const { theme } = useContext(ThemeContext);
|
||||||
const formattedExpiration = formatExpiration(apiKey.expiresAt || null);
|
const formattedExpiration = formatExpiration(apiKey.expiresAt || null);
|
||||||
|
|
||||||
@@ -43,7 +45,9 @@ export const SettingsApiKeysFieldItemTableRow = ({
|
|||||||
textOverflow="ellipsis"
|
textOverflow="ellipsis"
|
||||||
clickable
|
clickable
|
||||||
>
|
>
|
||||||
<StyledEllipsisLabel>{apiKey.name}</StyledEllipsisLabel>
|
<StyledEllipsisLabel>
|
||||||
|
{apiKey.name || t`Unnamed API Key`}
|
||||||
|
</StyledEllipsisLabel>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell
|
<TableCell
|
||||||
|
|||||||
+13
-7
@@ -201,12 +201,18 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
const regenerateApiKey = async () => {
|
const regenerateApiKey = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
if (isNonEmptyString(apiKey?.name)) {
|
if (isDefined(apiKey)) {
|
||||||
|
if (!isNonEmptyString(apiKeyName)) {
|
||||||
|
enqueueErrorSnackBar({
|
||||||
|
message: t`API key name cannot be empty`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
const newExpiresAt = computeNewExpirationDate(
|
const newExpiresAt = computeNewExpirationDate(
|
||||||
apiKey?.expiresAt,
|
apiKey.expiresAt,
|
||||||
apiKey?.createdAt,
|
apiKey.createdAt,
|
||||||
);
|
);
|
||||||
const newApiKey = await createIntegration(apiKey?.name, newExpiresAt);
|
const newApiKey = await createIntegration(apiKeyName, newExpiresAt);
|
||||||
await deleteIntegration(false);
|
await deleteIntegration(false);
|
||||||
|
|
||||||
if (isNonEmptyString(newApiKey?.token)) {
|
if (isNonEmptyString(newApiKey?.token)) {
|
||||||
@@ -233,9 +239,9 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{apiKey?.name && (
|
{isDefined(apiKey) && (
|
||||||
<SubMenuTopBarContainer
|
<SubMenuTopBarContainer
|
||||||
title={apiKey?.name}
|
title={apiKey.name || t`Unnamed API Key`}
|
||||||
links={[
|
links={[
|
||||||
{
|
{
|
||||||
children: t`Workspace`,
|
children: t`Workspace`,
|
||||||
@@ -245,7 +251,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
|||||||
children: t`APIs & Webhooks`,
|
children: t`APIs & Webhooks`,
|
||||||
href: getSettingsPath(SettingsPath.ApiWebhooks),
|
href: getSettingsPath(SettingsPath.ApiWebhooks),
|
||||||
},
|
},
|
||||||
{ children: apiKey?.name },
|
{ children: apiKey.name || t`Unnamed API Key` },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SettingsPageContainer>
|
<SettingsPageContainer>
|
||||||
|
|||||||
+7
-5
@@ -1,5 +1,5 @@
|
|||||||
import { addDays } from 'date-fns';
|
import { addDays } from 'date-fns';
|
||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||||
@@ -10,6 +10,7 @@ import { apiKeyTokenFamilyState } from '@/settings/developers/states/apiKeyToken
|
|||||||
import { Select } from '@/ui/input/components/Select';
|
import { Select } from '@/ui/input/components/Select';
|
||||||
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
||||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||||
|
import { useMutation, useQuery } from '@apollo/client/react';
|
||||||
import { useLingui } from '@lingui/react/macro';
|
import { useLingui } from '@lingui/react/macro';
|
||||||
import { useStore } from 'jotai';
|
import { useStore } from 'jotai';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
@@ -17,7 +18,6 @@ import { SettingsPath } from 'twenty-shared/types';
|
|||||||
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
|
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
|
||||||
import { H2Title } from 'twenty-ui/display';
|
import { H2Title } from 'twenty-ui/display';
|
||||||
import { Section } from 'twenty-ui/layout';
|
import { Section } from 'twenty-ui/layout';
|
||||||
import { useMutation, useQuery } from '@apollo/client/react';
|
|
||||||
import {
|
import {
|
||||||
CreateApiKeyDocument,
|
CreateApiKeyDocument,
|
||||||
GenerateApiKeyTokenDocument,
|
GenerateApiKeyTokenDocument,
|
||||||
@@ -70,6 +70,8 @@ export const SettingsDevelopersApiKeysNew = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
|
if (!formValues.name) return;
|
||||||
|
|
||||||
const expiresAt = addDays(
|
const expiresAt = addDays(
|
||||||
new Date(),
|
new Date(),
|
||||||
formValues.expirationDate ?? 30,
|
formValues.expirationDate ?? 30,
|
||||||
@@ -84,7 +86,7 @@ export const SettingsDevelopersApiKeysNew = () => {
|
|||||||
const { data: newApiKeyData } = await createApiKey({
|
const { data: newApiKeyData } = await createApiKey({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
name: formValues.name,
|
name: formValues.name.trim(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
roleId: roleIdToUse,
|
roleId: roleIdToUse,
|
||||||
},
|
},
|
||||||
@@ -115,7 +117,7 @@ export const SettingsDevelopersApiKeysNew = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const canSave = !!formValues.name && !!formValues.roleId && createApiKey;
|
const canSave = !!formValues.name && !!formValues.roleId;
|
||||||
|
|
||||||
if (rolesLoading) {
|
if (rolesLoading) {
|
||||||
return <SettingsSkeletonLoader />;
|
return <SettingsSkeletonLoader />;
|
||||||
@@ -137,7 +139,7 @@ export const SettingsDevelopersApiKeysNew = () => {
|
|||||||
]}
|
]}
|
||||||
actionButton={
|
actionButton={
|
||||||
<SaveAndCancelButtons
|
<SaveAndCancelButtons
|
||||||
isSaveDisabled={!isDefined(canSave)}
|
isSaveDisabled={!canSave}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
navigateSettings(SettingsPath.ApiWebhooks);
|
navigateSettings(SettingsPath.ApiWebhooks);
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user