fix glossary duplicates (#20801)

This commit is contained in:
Karan Hotchandani 2025-04-14 16:21:32 +05:30 committed by GitHub
parent 555be6d961
commit db41fc5ac5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 102 additions and 64 deletions

View File

@ -55,6 +55,7 @@ import {
deselectColumns, deselectColumns,
dragAndDropColumn, dragAndDropColumn,
dragAndDropTerm, dragAndDropTerm,
fillGlossaryTermDetails,
filterStatus, filterStatus,
goToAssetsTab, goToAssetsTab,
openColumnDropdown, openColumnDropdown,
@ -1384,6 +1385,55 @@ test.describe('Glossary tests', () => {
} }
}); });
test('Check for duplicate Glossary Term', async ({ browser }) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary('PW_TEST_GLOSSARY');
const glossaryTerm1 = new GlossaryTerm(
glossary1,
undefined,
'PW_TEST_TERM'
);
const glossaryTerm2 = new GlossaryTerm(
glossary1,
undefined,
'Pw_test_term'
);
await glossary1.create(apiContext);
try {
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await test.step('Create Glossary Term One', async () => {
await fillGlossaryTermDetails(page, glossaryTerm1.data, false, false);
const glossaryTermResponse = page.waitForResponse(
'/api/v1/glossaryTerms'
);
await page.click('[data-testid="save-glossary-term"]');
await glossaryTermResponse;
});
await test.step('Create Glossary Term Two', async () => {
await fillGlossaryTermDetails(page, glossaryTerm2.data, false, false);
const glossaryTermResponse = page.waitForResponse(
'/api/v1/glossaryTerms'
);
await page.click('[data-testid="save-glossary-term"]');
await glossaryTermResponse;
await expect(page.locator('#name_help')).toHaveText(
`A term with the name '${glossaryTerm2.data.name}' already exists in '${glossary1.data.name}' glossary.`
);
});
} finally {
await glossaryTerm1.delete(apiContext);
await glossary1.delete(apiContext);
await afterAction();
}
});
test.afterAll(async ({ browser }) => { test.afterAll(async ({ browser }) => {
const { afterAction, apiContext } = await performAdminLogin(browser); const { afterAction, apiContext } = await performAdminLogin(browser);
await user1.delete(apiContext); await user1.delete(apiContext);

View File

@ -73,6 +73,33 @@ const GlossaryTermModal: FC<Props> = ({
setSaving(true); setSaving(true);
try { try {
await onSave(values); await onSave(values);
} catch (error) {
if ((error as AxiosError)?.response?.status === 400) {
const errorMessage =
(error as AxiosError<{ message: string }>)?.response?.data?.message ??
'';
// Handle name duplication error
if (errorMessage.includes('already exists')) {
form.setFields([
{
name: 'name',
errors: [errorMessage],
},
]);
}
// Handle tag mutual exclusivity error
else if (errorMessage.includes('mutually exclusive')) {
form.setFields([
{
name: 'tags',
errors: [errorMessage],
},
]);
}
}
throw error;
} finally { } finally {
setSaving(false); setSaving(false);
} }
@ -84,7 +111,9 @@ const GlossaryTermModal: FC<Props> = ({
} else { } else {
setIsLoading(false); setIsLoading(false);
} }
!visible && form.resetFields(); if (!visible) {
form.resetFields();
}
}, [visible]); }, [visible]);
return ( return (

View File

@ -18,7 +18,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed';
import { HTTP_STATUS_CODE } from '../../constants/Auth.constants';
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider'; import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
import { import {
OperationPermission, OperationPermission,
@ -196,13 +195,14 @@ const GlossaryV1 = ({
currentData: GlossaryTerm, currentData: GlossaryTerm,
updatedData: GlossaryTerm updatedData: GlossaryTerm
) => { ) => {
try {
const jsonPatch = compare(currentData, updatedData); const jsonPatch = compare(currentData, updatedData);
const response = await patchGlossaryTerm(currentData?.id, jsonPatch); const response = await patchGlossaryTerm(currentData?.id, jsonPatch);
if (!response) { if (!response) {
throw t('server.entity-updating-error', { throw new Error(
t('server.entity-updating-error', {
entity: t('label.glossary-term'), entity: t('label.glossary-term'),
}); })
);
} else { } else {
updateGlossaryTermInStore({ updateGlossaryTermInStore({
...response, ...response,
@ -211,26 +211,6 @@ const GlossaryV1 = ({
}); });
setIsEditModalOpen(false); setIsEditModalOpen(false);
} }
} catch (error) {
if (
(error as AxiosError).response?.status === HTTP_STATUS_CODE.CONFLICT
) {
showErrorToast(
t('server.entity-already-exist', {
entity: t('label.glossary-term'),
entityPlural: t('label.glossary-term-lowercase-plural'),
name: updatedData.name,
})
);
} else {
showErrorToast(
error as AxiosError,
t('server.entity-updating-error', {
entity: t('label.glossary-term-lowercase'),
})
);
}
}
}; };
const onTermModalSuccess = useCallback( const onTermModalSuccess = useCallback(
@ -255,7 +235,6 @@ const GlossaryV1 = ({
); );
const handleGlossaryTermAdd = async (formData: GlossaryTermForm) => { const handleGlossaryTermAdd = async (formData: GlossaryTermForm) => {
try {
const term = await addGlossaryTerm({ const term = await addGlossaryTerm({
...formData, ...formData,
glossary: glossary:
@ -265,26 +244,6 @@ const GlossaryV1 = ({
}); });
onTermModalSuccess(term); onTermModalSuccess(term);
} catch (error) {
if (
(error as AxiosError).response?.status === HTTP_STATUS_CODE.CONFLICT
) {
showErrorToast(
t('server.entity-already-exist', {
entity: t('label.glossary-term'),
entityPlural: t('label.glossary-term-lowercase-plural'),
name: formData.name,
})
);
} else {
showErrorToast(
error as AxiosError,
t('server.create-entity-error', {
entity: t('label.glossary-term-lowercase'),
})
);
}
}
}; };
const handleGlossaryTermSave = async (formData: GlossaryTermForm) => { const handleGlossaryTermSave = async (formData: GlossaryTermForm) => {