Fix Glossary tree hierarchy state (#18703)

* fix the tree state on edit glossary term

* add playwright tests

* fix permission loading

* fix tests

* adding timeout for in review state

* adding timeout for in review state

* increasing timeout

* fix tests

* update tests

* add a poll for checking tasks created

* testing response

* adding limit

* fix tests

* change user for testing

* Catch Exceptions on the Consumer to avoid losing records

---------

Co-authored-by: Pablo Takara <pjt1991@gmail.com>
This commit is contained in:
Karan Hotchandani 2024-11-28 16:36:45 +05:30 committed by GitHub
parent 7877d5c14c
commit e22fc6ddeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 330 additions and 141 deletions

View File

@ -1,9 +1,12 @@
package org.openmetadata.service.governance.workflows; package org.openmetadata.service.governance.workflows;
import static org.openmetadata.schema.entity.events.SubscriptionDestination.SubscriptionType.GOVERNANCE_WORKFLOW_CHANGE_EVENT;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.openmetadata.schema.entity.events.EventSubscription; import org.openmetadata.schema.entity.events.EventSubscription;
import org.openmetadata.schema.entity.events.SubscriptionDestination; import org.openmetadata.schema.entity.events.SubscriptionDestination;
import org.openmetadata.schema.type.ChangeEvent; import org.openmetadata.schema.type.ChangeEvent;
@ -13,6 +16,7 @@ import org.openmetadata.schema.type.Include;
import org.openmetadata.service.Entity; import org.openmetadata.service.Entity;
import org.openmetadata.service.apps.bundles.changeEvent.Destination; import org.openmetadata.service.apps.bundles.changeEvent.Destination;
import org.openmetadata.service.events.errors.EventPublisherException; import org.openmetadata.service.events.errors.EventPublisherException;
import org.openmetadata.service.exception.CatalogExceptionMessage;
import org.openmetadata.service.resources.feeds.MessageParser; import org.openmetadata.service.resources.feeds.MessageParser;
@Slf4j @Slf4j
@ -46,22 +50,33 @@ public class WorkflowEventConsumer implements Destination<ChangeEvent> {
@Override @Override
public void sendMessage(ChangeEvent event) throws EventPublisherException { public void sendMessage(ChangeEvent event) throws EventPublisherException {
// NOTE: We are only consuming ENTITY related events. // NOTE: We are only consuming ENTITY related events.
EventType eventType = event.getEventType(); try {
EventType eventType = event.getEventType();
if (validEventTypes.contains(eventType)) { if (validEventTypes.contains(eventType)) {
String signal = String.format("%s-%s", event.getEntityType(), eventType.toString()); String signal = String.format("%s-%s", event.getEntityType(), eventType.toString());
EntityReference entityReference = EntityReference entityReference =
Entity.getEntityReferenceById(event.getEntityType(), event.getEntityId(), Include.ALL); Entity.getEntityReferenceById(event.getEntityType(), event.getEntityId(), Include.ALL);
MessageParser.EntityLink entityLink = MessageParser.EntityLink entityLink =
new MessageParser.EntityLink( new MessageParser.EntityLink(
event.getEntityType(), entityReference.getFullyQualifiedName()); event.getEntityType(), entityReference.getFullyQualifiedName());
Map<String, Object> variables = new HashMap<>(); Map<String, Object> variables = new HashMap<>();
variables.put("relatedEntity", entityLink.getLinkString()); variables.put("relatedEntity", entityLink.getLinkString());
WorkflowHandler.getInstance().triggerWithSignal(signal, variables); WorkflowHandler.getInstance().triggerWithSignal(signal, variables);
}
} catch (Exception exc) {
String message =
CatalogExceptionMessage.eventPublisherFailedToPublish(
GOVERNANCE_WORKFLOW_CHANGE_EVENT, event, exc.getMessage());
LOG.error(message);
throw new EventPublisherException(
CatalogExceptionMessage.eventPublisherFailedToPublish(
GOVERNANCE_WORKFLOW_CHANGE_EVENT, exc.getMessage()),
Pair.of(subscriptionDestination.getId(), event));
} }
} }

View File

@ -61,11 +61,13 @@ import {
selectActiveGlossaryTerm, selectActiveGlossaryTerm,
selectColumns, selectColumns,
toggleAllColumnsSelection, toggleAllColumnsSelection,
updateGlossaryTermDataFromTree,
validateGlossaryTerm, validateGlossaryTerm,
verifyAllColumns, verifyAllColumns,
verifyColumnsVisibility, verifyColumnsVisibility,
verifyGlossaryDetails, verifyGlossaryDetails,
verifyGlossaryTermAssets, verifyGlossaryTermAssets,
verifyTaskCreated,
} from '../../utils/glossary'; } from '../../utils/glossary';
import { sidebarClick } from '../../utils/sidebar'; import { sidebarClick } from '../../utils/sidebar';
import { TaskDetails } from '../../utils/task'; import { TaskDetails } from '../../utils/task';
@ -74,131 +76,133 @@ import { performUserLogin } from '../../utils/user';
const user1 = new UserClass(); const user1 = new UserClass();
const user2 = new UserClass(); const user2 = new UserClass();
const team = new TeamClass(); const team = new TeamClass();
const user3 = new UserClass();
test.describe('Glossary tests', () => { test.describe('Glossary tests', () => {
test.beforeAll(async ({ browser }) => { test.beforeAll(async ({ browser }) => {
const { afterAction, apiContext } = await performAdminLogin(browser); const { afterAction, apiContext } = await performAdminLogin(browser);
await user2.create(apiContext); await user2.create(apiContext);
await user1.create(apiContext); await user1.create(apiContext);
await user3.create(apiContext);
team.data.users = [user2.responseData.id]; team.data.users = [user2.responseData.id];
await team.create(apiContext); await team.create(apiContext);
await afterAction(); await afterAction();
}); });
test.fixme( test('Glossary & terms creation for reviewer as user', async ({
'Glossary & terms creation for reviewer as user', browser,
async ({ browser }) => { }) => {
test.slow(true); test.slow(true);
const { page, afterAction, apiContext } = await performAdminLogin( const { page, afterAction, apiContext } = await performAdminLogin(browser);
browser const { page: page1, afterAction: afterActionUser1 } =
); await performUserLogin(browser, user3);
const { page: page1, afterAction: afterActionUser1 } = const glossary1 = new Glossary();
await performUserLogin(browser, user1); glossary1.data.owners = [{ name: 'admin', type: 'user' }];
const glossary1 = new Glossary(); glossary1.data.mutuallyExclusive = true;
glossary1.data.owners = [{ name: 'admin', type: 'user' }]; glossary1.data.reviewers = [
glossary1.data.mutuallyExclusive = true; { name: `${user3.data.firstName}${user3.data.lastName}`, type: 'user' },
glossary1.data.reviewers = [ ];
{ name: `${user1.data.firstName}${user1.data.lastName}`, type: 'user' }, glossary1.data.terms = [new GlossaryTerm(glossary1)];
];
glossary1.data.terms = [new GlossaryTerm(glossary1)];
await test.step('Create Glossary', async () => { await test.step('Create Glossary', async () => {
await sidebarClick(page, SidebarItem.GLOSSARY); await sidebarClick(page, SidebarItem.GLOSSARY);
await createGlossary(page, glossary1.data, false); await createGlossary(page, glossary1.data, false);
await verifyGlossaryDetails(page, glossary1.data); await verifyGlossaryDetails(page, glossary1.data);
}); });
await test.step('Create Glossary Terms', async () => { await test.step('Create Glossary Terms', async () => {
await sidebarClick(page, SidebarItem.GLOSSARY); await sidebarClick(page, SidebarItem.GLOSSARY);
await createGlossaryTerms(page, glossary1.data); await createGlossaryTerms(page, glossary1.data);
}); });
await test.step( await test.step(
'Approve Glossary Term from Glossary Listing for reviewer user', 'Approve Glossary Term from Glossary Listing for reviewer user',
async () => { async () => {
await redirectToHomePage(page1); await redirectToHomePage(page1);
// wait for 15 seconds as the flowable which creates task is triggered every 10 seconds await sidebarClick(page1, SidebarItem.GLOSSARY);
await page1.waitForTimeout(15000); await selectActiveGlossary(page1, glossary1.data.name);
await sidebarClick(page1, SidebarItem.GLOSSARY); await verifyTaskCreated(
await selectActiveGlossary(page1, glossary1.data.name); page1,
glossary1.data.fullyQualifiedName,
glossary1.data.terms[0].data
);
await approveGlossaryTermTask(page1, glossary1.data.terms[0].data); await approveGlossaryTermTask(page1, glossary1.data.terms[0].data);
await redirectToHomePage(page1); await redirectToHomePage(page1);
await sidebarClick(page1, SidebarItem.GLOSSARY); await sidebarClick(page1, SidebarItem.GLOSSARY);
await selectActiveGlossary(page1, glossary1.data.name); await selectActiveGlossary(page1, glossary1.data.name);
await validateGlossaryTerm( await validateGlossaryTerm(
page1, page1,
glossary1.data.terms[0].data, glossary1.data.terms[0].data,
'Approved' 'Approved'
); );
await afterActionUser1(); await afterActionUser1();
} }
); );
await glossary1.delete(apiContext); await glossary1.delete(apiContext);
await afterAction(); await afterAction();
} });
);
test.fixme( test('Glossary & terms creation for reviewer as team', async ({
'Glossary & terms creation for reviewer as team', browser,
async ({ browser }) => { }) => {
test.slow(true); test.slow(true);
const { page, afterAction, apiContext } = await performAdminLogin( const { page, afterAction, apiContext } = await performAdminLogin(browser);
browser const { page: page1, afterAction: afterActionUser1 } =
); await performUserLogin(browser, user2);
const { page: page1, afterAction: afterActionUser1 } =
await performUserLogin(browser, user2);
const glossary2 = new Glossary(); const glossary2 = new Glossary();
glossary2.data.owners = [{ name: 'admin', type: 'user' }]; glossary2.data.owners = [{ name: 'admin', type: 'user' }];
glossary2.data.reviewers = [ glossary2.data.reviewers = [{ name: team.data.displayName, type: 'team' }];
{ name: team.data.displayName, type: 'team' }, glossary2.data.terms = [new GlossaryTerm(glossary2)];
];
glossary2.data.terms = [new GlossaryTerm(glossary2)];
await test.step('Create Glossary', async () => { await test.step('Create Glossary', async () => {
await sidebarClick(page, SidebarItem.GLOSSARY); await sidebarClick(page, SidebarItem.GLOSSARY);
await createGlossary(page, glossary2.data, false); await createGlossary(page, glossary2.data, false);
await verifyGlossaryDetails(page, glossary2.data); await verifyGlossaryDetails(page, glossary2.data);
}); });
await test.step('Create Glossary Terms', async () => { await test.step('Create Glossary Terms', async () => {
await redirectToHomePage(page); await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.GLOSSARY); await sidebarClick(page, SidebarItem.GLOSSARY);
await createGlossaryTerms(page, glossary2.data); await createGlossaryTerms(page, glossary2.data);
}); });
await test.step( await test.step(
'Approve Glossary Term from Glossary Listing for reviewer team', 'Approve Glossary Term from Glossary Listing for reviewer team',
async () => { async () => {
await redirectToHomePage(page1); await redirectToHomePage(page1);
// wait for 15 seconds as the flowable which creates task is triggered every 10 seconds await sidebarClick(page1, SidebarItem.GLOSSARY);
await page1.waitForTimeout(15000); await selectActiveGlossary(page1, glossary2.data.name);
await sidebarClick(page1, SidebarItem.GLOSSARY);
await selectActiveGlossary(page1, glossary2.data.name);
await approveGlossaryTermTask(page1, glossary2.data.terms[0].data);
await redirectToHomePage(page1); await verifyTaskCreated(
await sidebarClick(page1, SidebarItem.GLOSSARY); page1,
await selectActiveGlossary(page1, glossary2.data.name); glossary2.data.fullyQualifiedName,
await validateGlossaryTerm( glossary2.data.terms[0].data
page1, );
glossary2.data.terms[0].data,
'Approved'
);
await afterActionUser1(); await approveGlossaryTermTask(page1, glossary2.data.terms[0].data);
}
);
await glossary2.delete(apiContext); await redirectToHomePage(page1);
await afterAction(); await sidebarClick(page1, SidebarItem.GLOSSARY);
} await selectActiveGlossary(page1, glossary2.data.name);
); await validateGlossaryTerm(
page1,
glossary2.data.terms[0].data,
'Approved'
);
await afterActionUser1();
}
);
await glossary2.delete(apiContext);
await afterAction();
});
test('Update Glossary and Glossary Term', async ({ browser }) => { test('Update Glossary and Glossary Term', async ({ browser }) => {
test.slow(true); test.slow(true);
@ -789,9 +793,8 @@ test.describe('Glossary tests', () => {
'[data-testid="viewer-container"]' '[data-testid="viewer-container"]'
); );
await expect(viewerContainerText).toContain('Updated description'); expect(viewerContainerText).toContain('Updated description');
} finally { } finally {
await user1.delete(apiContext);
await glossary1.delete(apiContext); await glossary1.delete(apiContext);
await afterAction(); await afterAction();
} }
@ -836,9 +839,8 @@ test.describe('Glossary tests', () => {
'[data-testid="viewer-container"]' '[data-testid="viewer-container"]'
); );
await expect(viewerContainerText).toContain('Updated description'); expect(viewerContainerText).toContain('Updated description');
} finally { } finally {
await user1.delete(apiContext);
await glossaryTerm1.delete(apiContext); await glossaryTerm1.delete(apiContext);
await glossary1.delete(apiContext); await glossary1.delete(apiContext);
await afterAction(); await afterAction();
@ -870,6 +872,7 @@ test.describe('Glossary tests', () => {
} finally { } finally {
await glossary1.delete(apiContext); await glossary1.delete(apiContext);
await afterAction(); await afterAction();
await afterActionUser1();
} }
}); });
@ -1063,10 +1066,60 @@ test.describe('Glossary tests', () => {
} }
}); });
test('Glossary Term Update in Glossary Page should persist tree', async ({
browser,
}) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const glossaryTerm1 = new GlossaryTerm(glossary1);
await glossary1.create(apiContext);
await glossaryTerm1.create(apiContext);
const glossaryTerm2 = new GlossaryTerm(
glossary1,
glossaryTerm1.responseData.fullyQualifiedName
);
await glossaryTerm2.create(apiContext);
const glossaryTerm3 = new GlossaryTerm(
glossary1,
glossaryTerm2.responseData.fullyQualifiedName
);
await glossaryTerm3.create(apiContext);
try {
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await page.getByTestId('expand-collapse-all-button').click();
await expect(
page.getByRole('cell', { name: glossaryTerm1.data.displayName })
).toBeVisible();
await expect(
page.getByRole('cell', { name: glossaryTerm2.data.displayName })
).toBeVisible();
await expect(
page.getByRole('cell', { name: glossaryTerm3.data.displayName })
).toBeVisible();
await updateGlossaryTermDataFromTree(
page,
glossaryTerm2.responseData.fullyQualifiedName
);
} finally {
await glossaryTerm3.delete(apiContext);
await glossaryTerm2.delete(apiContext);
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);
await user2.delete(apiContext); await user2.delete(apiContext);
await user3.create(apiContext);
await team.delete(apiContext); await team.delete(apiContext);
await afterAction(); await afterAction();
}); });

View File

@ -37,6 +37,12 @@ import { addMultiOwner } from './entity';
import { sidebarClick } from './sidebar'; import { sidebarClick } from './sidebar';
import { TaskDetails, TASK_OPEN_FETCH_LINK } from './task'; import { TaskDetails, TASK_OPEN_FETCH_LINK } from './task';
type TaskEntity = {
entityRef: {
name: string;
};
};
const GLOSSARY_NAME_VALIDATION_ERROR = 'Name size must be between 1 and 128'; const GLOSSARY_NAME_VALIDATION_ERROR = 'Name size must be between 1 and 128';
export const descriptionBox = export const descriptionBox =
@ -469,8 +475,47 @@ export const fillGlossaryTermDetails = async (
} }
}; };
const validateGlossaryTermTask = async (page: Page, term: GlossaryTermData) => { export const verifyTaskCreated = async (
page: Page,
glossaryFqn: string,
glossaryTermData: GlossaryTermData
) => {
const { apiContext } = await getApiContext(page);
const entityLink = encodeURIComponent(`<#E::glossary::${glossaryFqn}>`);
await expect
.poll(
async () => {
const response = await apiContext
.get(
`/api/v1/feed?entityLink=${entityLink}&type=Task&taskStatus=Open`
)
.then((res) => res.json());
const arr = response.data.map(
(item: TaskEntity) => item.entityRef.name
);
return arr;
},
{
// Custom expect message for reporting, optional.
message: 'To get the last run execution status as success',
timeout: 200_000,
intervals: [30_000],
}
)
.toContain(glossaryTermData.name);
};
export const validateGlossaryTermTask = async (
page: Page,
term: GlossaryTermData
) => {
const taskCountRes = page.waitForResponse('/api/v1/feed/count?*');
await page.click('[data-testid="activity_feed"]'); await page.click('[data-testid="activity_feed"]');
await taskCountRes;
const taskFeeds = page.waitForResponse(TASK_OPEN_FETCH_LINK); const taskFeeds = page.waitForResponse(TASK_OPEN_FETCH_LINK);
await page await page
.getByTestId('global-setting-left-panel') .getByTestId('global-setting-left-panel')
@ -504,6 +549,37 @@ export const approveGlossaryTermTask = async (
await toastNotification(page, /Task resolved successfully/); await toastNotification(page, /Task resolved successfully/);
}; };
// Show the glossary term edit modal from glossary page tree.
// Update the description and verify the changes.
export const updateGlossaryTermDataFromTree = async (
page: Page,
termFqn: string
) => {
// eslint-disable-next-line no-useless-escape
const escapedFqn = termFqn.replace(/\"/g, '\\"');
const termRow = page.locator(`[data-row-key="${escapedFqn}"]`);
await termRow.getByTestId('edit-button').click();
await page.waitForSelector('[role="dialog"].edit-glossary-modal');
await expect(
page.locator('[role="dialog"].edit-glossary-modal')
).toBeVisible();
await expect(page.locator('.ant-modal-title')).toContainText(
'Edit Glossary Term'
);
await page.locator(descriptionBox).fill('Updated description');
const glossaryTermResponse = page.waitForResponse('/api/v1/glossaryTerms/*');
await page.getByTestId('save-glossary-term').click();
await glossaryTermResponse;
await expect(
termRow.getByRole('cell', { name: 'Updated description' })
).toBeVisible();
};
export const validateGlossaryTerm = async ( export const validateGlossaryTerm = async (
page: Page, page: Page,
term: GlossaryTermData, term: GlossaryTermData,
@ -516,13 +592,6 @@ export const validateGlossaryTerm = async (
await expect(page.locator(termSelector)).toContainText(term.name); await expect(page.locator(termSelector)).toContainText(term.name);
await expect(page.locator(statusSelector)).toContainText(status); await expect(page.locator(statusSelector)).toContainText(status);
if (status === 'Draft') {
// wait for 15 seconds as the flowable which creates task is triggered every 10 seconds
await page.waitForTimeout(15000);
await validateGlossaryTermTask(page, term);
await page.click('[data-testid="terms"]');
}
}; };
export const createGlossaryTerm = async ( export const createGlossaryTerm = async (

View File

@ -270,7 +270,7 @@ const GlossaryDetails = ({
{getWidgetFromKeyInternal(widget)} {getWidgetFromKeyInternal(widget)}
</div> </div>
)); ));
}, [tagsWidget, layout]); }, [tagsWidget, layout, permissions, termsLoading]);
const detailsContent = useMemo(() => { const detailsContent = useMemo(() => {
return ( return (

View File

@ -114,6 +114,10 @@ const GlossaryTermTab = ({
const [isTableLoading, setIsTableLoading] = useState(false); const [isTableLoading, setIsTableLoading] = useState(false);
const [isTableHovered, setIsTableHovered] = useState(false); const [isTableHovered, setIsTableHovered] = useState(false);
const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]); const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
const listOfVisibleColumns = useMemo(() => {
return ['name', 'description', 'owners', 'status', 'new-term'];
}, []);
const [isStatusDropdownVisible, setIsStatusDropdownVisible] = const [isStatusDropdownVisible, setIsStatusDropdownVisible] =
useState<boolean>(false); useState<boolean>(false);
const statusOptions = useMemo( const statusOptions = useMemo(
@ -123,7 +127,7 @@ const GlossaryTermTab = ({
); );
const [statusDropdownSelection, setStatusDropdownSelections] = useState< const [statusDropdownSelection, setStatusDropdownSelections] = useState<
string[] string[]
>(['Approved', 'Draft']); >([Status.Approved, Status.Draft, Status.InReview]);
const [selectedStatus, setSelectedStatus] = useState<string[]>([ const [selectedStatus, setSelectedStatus] = useState<string[]>([
...statusDropdownSelection, ...statusDropdownSelection,
]); ]);
@ -296,11 +300,7 @@ const GlossaryTermTab = ({
} }
return data; return data;
}, [glossaryTerms, permissions]); }, [permissions]);
const listOfVisibleColumns = useMemo(() => {
return ['name', 'description', 'owners', 'status', 'new-term'];
}, []);
const defaultCheckedList = useMemo( const defaultCheckedList = useMemo(
() => () =>
@ -353,7 +353,7 @@ const GlossaryTermTab = ({
return aIndex - bIndex; return aIndex - bIndex;
}), }),
[newColumns] [options, newColumns]
); );
const handleColumnSelectionDropdownSave = useCallback(() => { const handleColumnSelectionDropdownSave = useCallback(() => {

View File

@ -41,6 +41,7 @@ import {
patchGlossaryTerm, patchGlossaryTerm,
} from '../../rest/glossaryAPI'; } from '../../rest/glossaryAPI';
import { getEntityDeleteMessage } from '../../utils/CommonUtils'; import { getEntityDeleteMessage } from '../../utils/CommonUtils';
import { updateGlossaryTermByFqn } from '../../utils/GlossaryUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
import { useActivityFeedProvider } from '../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; import { useActivityFeedProvider } from '../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
@ -84,6 +85,7 @@ const GlossaryV1 = ({
const { getEntityPermission } = usePermissionProvider(); const { getEntityPermission } = usePermissionProvider();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [isTermsLoading, setIsTermsLoading] = useState(false); const [isTermsLoading, setIsTermsLoading] = useState(false);
const [isPermissionLoading, setIsPermissionLoading] = useState(false);
const [isDelete, setIsDelete] = useState<boolean>(false); const [isDelete, setIsDelete] = useState<boolean>(false);
@ -97,7 +99,8 @@ const GlossaryV1 = ({
const [editMode, setEditMode] = useState(false); const [editMode, setEditMode] = useState(false);
const { activeGlossary, setGlossaryChildTerms } = useGlossaryStore(); const { activeGlossary, glossaryChildTerms, setGlossaryChildTerms } =
useGlossaryStore();
const { id, fullyQualifiedName } = activeGlossary ?? {}; const { id, fullyQualifiedName } = activeGlossary ?? {};
@ -206,6 +209,17 @@ const GlossaryV1 = ({
setIsEditModalOpen(true); setIsEditModalOpen(true);
}; };
const updateGlossaryTermInStore = (updatedTerm: GlossaryTerm) => {
const clonedTerms = cloneDeep(glossaryChildTerms);
const updatedGlossaryTerms = updateGlossaryTermByFqn(
clonedTerms,
updatedTerm.fullyQualifiedName ?? '',
updatedTerm as ModifiedGlossary
);
setGlossaryChildTerms(updatedGlossaryTerms);
};
const updateGlossaryTerm = async ( const updateGlossaryTerm = async (
currentData: GlossaryTerm, currentData: GlossaryTerm,
updatedData: GlossaryTerm updatedData: GlossaryTerm
@ -217,8 +231,10 @@ const GlossaryV1 = ({
throw t('server.entity-updating-error', { throw t('server.entity-updating-error', {
entity: t('label.glossary-term'), entity: t('label.glossary-term'),
}); });
} else {
updateGlossaryTermInStore(response);
setIsEditModalOpen(false);
} }
onTermModalSuccess();
} catch (error) { } catch (error) {
if ( if (
(error as AxiosError).response?.status === HTTP_STATUS_CODE.CONFLICT (error as AxiosError).response?.status === HTTP_STATUS_CODE.CONFLICT
@ -336,18 +352,29 @@ const GlossaryV1 = ({
shouldRefreshTerms && loadGlossaryTerms(true); shouldRefreshTerms && loadGlossaryTerms(true);
}; };
const initPermissions = async () => {
setIsPermissionLoading(true);
const permissionFetch = isGlossaryActive
? fetchGlossaryPermission
: fetchGlossaryTermPermission;
try {
if (isVersionsView) {
isGlossaryActive
? setGlossaryPermission(VERSION_VIEW_GLOSSARY_PERMISSION)
: setGlossaryTermPermission(VERSION_VIEW_GLOSSARY_PERMISSION);
} else {
await permissionFetch();
}
} finally {
setIsPermissionLoading(false);
}
};
useEffect(() => { useEffect(() => {
if (id && !action) { if (id && !action) {
loadGlossaryTerms(); loadGlossaryTerms();
if (isGlossaryActive) { initPermissions();
isVersionsView
? setGlossaryPermission(VERSION_VIEW_GLOSSARY_PERMISSION)
: fetchGlossaryPermission();
} else {
isVersionsView
? setGlossaryTermPermission(VERSION_VIEW_GLOSSARY_PERMISSION)
: fetchGlossaryTermPermission();
}
} }
}, [id, isGlossaryActive, isVersionsView, action]); }, [id, isGlossaryActive, isVersionsView, action]);
@ -355,8 +382,9 @@ const GlossaryV1 = ({
<ImportGlossary glossaryName={selectedData.fullyQualifiedName ?? ''} /> <ImportGlossary glossaryName={selectedData.fullyQualifiedName ?? ''} />
) : ( ) : (
<> <>
{isLoading && <Loader />} {(isLoading || isPermissionLoading) && <Loader />}
{!isLoading && {!isLoading &&
!isPermissionLoading &&
!isEmpty(selectedData) && !isEmpty(selectedData) &&
(isGlossaryActive ? ( (isGlossaryActive ? (
<GlossaryDetails <GlossaryDetails

View File

@ -163,6 +163,30 @@ export const getGlossaryBreadcrumbs = (fqn: string) => {
return breadcrumbList; return breadcrumbList;
}; };
export const updateGlossaryTermByFqn = (
glossaryTerms: ModifiedGlossary[],
fqn: string,
newValue: ModifiedGlossary
): ModifiedGlossary[] => {
return glossaryTerms.map((term) => {
if (term.fullyQualifiedName === fqn) {
return newValue;
}
if (term.children) {
return {
...term,
children: updateGlossaryTermByFqn(
term.children as ModifiedGlossary[],
fqn,
newValue
),
};
}
return term;
}) as ModifiedGlossary[];
};
// This function finds and gives you the glossary term you're looking for. // This function finds and gives you the glossary term you're looking for.
// You can then use this term or update its information in the Glossary or Term with it's reference created // You can then use this term or update its information in the Glossary or Term with it's reference created
// Reference will only be created if withReference is true // Reference will only be created if withReference is true