Repository: open-metadata/OpenMetadata
Pull request #20091: MINOR: fix wrong suggestion count being fetched on table change
Author: login: Ashish8689
Labels: name: UI
description: UI specific issues
name: safe to test
description: Add this label to run secure Github workflows on PRs
Created: 2025-03-05T13:28:27Z | Merged: 2025-03-06T13:23:07Z

<!--
Thank you for your contribution!
Unless your change is trivial, please create an issue to discuss the change before creating a PR.
-->

### Describe your changes:

<!--
Short blurb explaining:
- What changes did you make?
- Why did you make them?
- How did you test your changes?
-->

- I worked fixing wrong suggestion count being fetched on Table entity if switch to another table from one table. Due to Provider being wrapped on the TableDetailPage!

<!-- For frontend related change, please add screenshots and/or videos of your changes preview! -->

Issue : 


https://github.com/user-attachments/assets/217e723e-2843-46aa-9ae3-023a01a0c811



Resolved : 

https://github.com/user-attachments/assets/04013c15-c947-4e34-b9ed-1ec42e9e786c



#
### Type of change:
<!-- You should choose 1 option and delete options that aren't relevant -->
- [x] Bug fix
- [x] Improvement
- [ ] New feature
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation

#
### Checklist:
<!-- add an x in [] if done, don't mark items that you didn't do !-->
- [x] I have read the [**CONTRIBUTING**](https://docs.open-metadata.org/developers/contribute) document.
- [ ] My PR title is `Fixes <issue-number>: <short explanation>`
- [ ] I have commented on my code, particularly in hard-to-understand areas. 
- [ ] For JSON Schema changes: I updated the migration scripts or explained why it is not needed.

<!-- Based on the type(s) of your change, uncomment the required checklist 👇 -->

<!-- Bug fix
- [ ] I have added a test that covers the exact scenario we are fixing. For complex issues, comment the issue number in the test for future reference.
-->

<!-- Improvement
- [ ] I have added tests around the new logic.
- [ ] For connector/ingestion changes: I updated the documentation.
-->

<!-- New feature
- [ ] The issue properly describes why the new feature is needed, what's the goal, and how we are building it. Any discussion
    or decision-making process is reflected in the issue.
- [ ] I have updated the documentation.
- [ ] I have added tests around the new logic.
-->

<!-- Breaking change
- [ ] I have added the tag `Backward-Incompatible-Change`.
-->



Diff:
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts
index 9a86f0f38b0d..a31e41b307f5 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DescriptionSuggestion.spec.ts
@@ -19,6 +19,7 @@ import { createTableSuggestions } from '../../utils/suggestions';
 import { performUserLogin } from '../../utils/user';
 
 const table = new TableClass();
+const table2 = new TableClass();
 const user1 = new UserClass();
 const user2 = new UserClass();
 let entityLinkList: string[];
@@ -29,6 +30,7 @@ test.describe('Description Suggestions Table Entity', () => {
   test.beforeAll('Setup pre-requests', async ({ browser }) => {
     const { afterAction, apiContext } = await performAdminLogin(browser);
     await table.create(apiContext);
+    await table2.create(apiContext);
 
     entityLinkList = table.entityLinkColumnsName.map(
       (entityLinkName) =>
@@ -48,6 +50,7 @@ test.describe('Description Suggestions Table Entity', () => {
   test.afterAll('Cleanup', async ({ browser }) => {
     const { afterAction, apiContext } = await performAdminLogin(browser);
     await table.delete(apiContext);
+    await table2.delete(apiContext);
     await user1.delete(apiContext);
     await user2.delete(apiContext);
     await afterAction();
@@ -248,4 +251,29 @@ test.describe('Description Suggestions Table Entity', () => {
     await afterAction2();
     await afterAction3();
   });
+
+  test('Should fetch initial 10 suggestions on entity change from table1 to table2', async ({
+    browser,
+  }) => {
+    // Jumping from one table to another table to check if the suggestions are fetched correctly
+    // due to provider boundary on entity.
+
+    const { page, afterAction } = await performAdminLogin(browser);
+
+    await redirectToHomePage(page);
+
+    const suggestionFetchCallResponse = page.waitForResponse(
+      '/api/v1/suggestions?entityFQN=*&limit=10'
+    );
+    await table2.visitEntityPage(page);
+    await suggestionFetchCallResponse;
+
+    const suggestionFetchCallResponse2 = page.waitForResponse(
+      '/api/v1/suggestions?entityFQN=*&limit=10'
+    );
+    await table.visitEntityPage(page);
+    await suggestionFetchCallResponse2;
+
+    await afterAction();
+  });
 });
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
index 49168907b8d0..2f8952896e08 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
@@ -24,6 +24,7 @@ import React, {
   useState,
 } from 'react';
 import { useTranslation } from 'react-i18next';
+import { PAGE_SIZE } from '../../../constants/constants';
 import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
 import {
   Suggestion,
@@ -62,48 +63,51 @@ const SuggestionsProvider = ({ children }: { children?: ReactNode }) => {
   const publish = usePub();
 
   const [loading, setLoading] = useState(false);
-  const [suggestionLimit, setSuggestionLimit] = useState<number>(10);
+  const [suggestionLimit, setSuggestionLimit] = useState<number>(PAGE_SIZE);
   const refreshEntity = useRef<(suggestion: Suggestion) => void>();
   const { permissions } = usePermissionProvider();
 
-  const fetchSuggestions = useCallback(async () => {
-    setLoading(true);
-    try {
-      const { data, paging } = await getSuggestionsList({
-        entityFQN: entityFqn,
-        limit: suggestionLimit,
-      });
-      setSuggestions(data);
-      setSuggestionLimit(paging.total);
-
-      const allUsersData = data.map(
-        (suggestion) => suggestion.createdBy as EntityReference
-      );
-      const uniqueUsers = uniqWith(allUsersData, isEqual);
-      setAllSuggestionsUsers(uniqueUsers);
-
-      const groupedSuggestions = data.reduce((acc, suggestion) => {
-        const createdBy = suggestion?.createdBy?.name ?? '';
-        if (!acc.has(createdBy)) {
-          acc.set(createdBy, []);
-        }
-        acc.get(createdBy)?.push(suggestion);
-
-        return acc;
-      }, new Map() as Map<string, Suggestion[]>);
-
-      setSuggestionsByUser(groupedSuggestions);
-    } catch (err) {
-      showErrorToast(
-        err as AxiosError,
-        t('server.entity-fetch-error', {
-          entity: t('label.lineage-data-lowercase'),
-        })
-      );
-    } finally {
-      setLoading(false);
-    }
-  }, [entityFqn, suggestionLimit]);
+  const fetchSuggestions = useCallback(
+    async (limit?: number) => {
+      setLoading(true);
+      try {
+        const { data, paging } = await getSuggestionsList({
+          entityFQN: entityFqn,
+          limit: limit ?? suggestionLimit,
+        });
+        setSuggestions(data);
+        setSuggestionLimit(paging.total);
+
+        const allUsersData = data.map(
+          (suggestion) => suggestion.createdBy as EntityReference
+        );
+        const uniqueUsers = uniqWith(allUsersData, isEqual);
+        setAllSuggestionsUsers(uniqueUsers);
+
+        const groupedSuggestions = data.reduce((acc, suggestion) => {
+          const createdBy = suggestion?.createdBy?.name ?? '';
+          if (!acc.has(createdBy)) {
+            acc.set(createdBy, []);
+          }
+          acc.get(createdBy)?.push(suggestion);
+
+          return acc;
+        }, new Map() as Map<string, Suggestion[]>);
+
+        setSuggestionsByUser(groupedSuggestions);
+      } catch (err) {
+        showErrorToast(
+          err as AxiosError,
+          t('server.entity-fetch-error', {
+            entity: t('label.lineage-data-lowercase'),
+          })
+        );
+      } finally {
+        setLoading(false);
+      }
+    },
+    [entityFqn, suggestionLimit]
+  );
 
   const acceptRejectSuggestion = useCallback(
     async (suggestion: Suggestion, status: SuggestionAction) => {
@@ -166,7 +170,7 @@ const SuggestionsProvider = ({ children }: { children?: ReactNode }) => {
 
   useEffect(() => {
     if (!isEmpty(permissions) && !isEmpty(entityFqn)) {
-      fetchSuggestions();
+      fetchSuggestions(PAGE_SIZE);
     }
   }, [entityFqn, permissions]);
 


Reviews:
user: login: chirag-madlani
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T05:10:34Z
user: login: Ashish8689
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T05:29:25Z
user: login: chirag-madlani
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-06T13:16:01Z

Review comments:
diff_hunk: @@ -62,48 +63,51 @@ const SuggestionsProvider = ({ children }: { children?: ReactNode }) => {
   const publish = usePub();
 
   const [loading, setLoading] = useState(false);
-  const [suggestionLimit, setSuggestionLimit] = useState<number>(10);
+  const [suggestionLimit, setSuggestionLimit] = useState<number>(PAGE_SIZE);
path: openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
user: login: chirag-madlani
body: Why we are using state and not using usePaging hook?
created_at: 2025-03-06T05:10:30Z
author_association: COLLABORATOR
diff_hunk: @@ -62,48 +63,51 @@ const SuggestionsProvider = ({ children }: { children?: ReactNode }) => {
   const publish = usePub();
 
   const [loading, setLoading] = useState(false);
-  const [suggestionLimit, setSuggestionLimit] = useState<number>(10);
+  const [suggestionLimit, setSuggestionLimit] = useState<number>(PAGE_SIZE);
path: openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx
user: login: Ashish8689
body: Here we don't require any Next, Previous, or limit changing mechanism. State is only set to get all the value from total and fetch them single time. On the user action.
created_at: 2025-03-06T05:29:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-05T14:49:12Z
body: <!-- Jest Coverage Comment: ui-coverage-tests -->
# Jest test Coverage

## UI tests summary

| Lines | Statements | Branches | Functions |
| --- | --- | --- | --- |
| <a href="https://github.com/open-metadata/OpenMetadata/blob//README.md"><img alt="Coverage: 63%" src="https://img.shields.io/badge/Coverage-63%25-yellow.svg" /></a><br/> | 63.55% (42119/66277) | 38.7% (16527/42707) | 43.1% (5055/11728) |

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-03-05T17:46:17Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-metadata-ui&pullRequest=20091) **Quality Gate passed for 'open-metadata-ui'**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=open-metadata-ui&pullRequest=20091&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-metadata-ui&pullRequest=20091&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-metadata-ui&pullRequest=20091&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-metadata-ui&pullRequest=20091&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-metadata-ui&pullRequest=20091&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-metadata-ui&pullRequest=20091)


author_association: NONE