Repository: featureform/featureform (language: python)

Issue:
[Bug]: A bad "type" browser URL leads to an NPE 
### Expected Behavior

When a faulty or non-existent browser URL is entered, the page should gracefully display the error to the user. 

### Actual Behavior

Currently the app displays an error when running locally in node, or crashes back to the homepage with faulty breadcrumb artifacts: 

<img width="512" alt="Screenshot 2023-04-25 at 1 41 52 PM" src="https://user-images.githubusercontent.com/34227093/234372003-e132e8f7-ac0b-4421-9043-ab43e4faee5f.png">



https://user-images.githubusercontent.com/34227093/234372744-6b5dea1a-bf03-4efe-859a-c18235f8d2ac.mov




### Steps To Reproduce

1. Enter an non-existent resource path. 
2. If running on NPM locally, an error will display. 
3. If running in docker, the app crashes back to the homepage with faulty breadcrumbs displaying. 

### What mode are you running Featureform in?

Local

### What version of Python are you running?

3.7

### Featureform Python Package Version

0.7.3

### Featureform Helm Chart Version

_No response_

### Kubernetes Version

_No response_

### Relevant log output

_No response_


Discussion / hints:

Fix patch:
diff --git a/dashboard/src/components/entitypage/EntityPage.js b/dashboard/src/components/entitypage/EntityPage.js
index 7f249f23db..d36c828dd1 100644
--- a/dashboard/src/components/entitypage/EntityPage.js
+++ b/dashboard/src/components/entitypage/EntityPage.js
@@ -38,10 +38,6 @@ const LoadingDots = () => {
   );
 };
 
-const checkIfEmpty = (object) => {
-  return Object.keys(object).length === 0 && object.constructor === Object;
-};
-
 const fetchNotFound = (object) => {
   return !object?.resources?.name && !object?.resources?.type
 }
@@ -50,18 +46,18 @@ const EntityPage = ({ api, entityPage, activeVariants, type, entity, ...props })
   let resourceType = Resource[Resource.pathToType[type]];
   const fetchEntity = props.fetch;
 
-  useEffect(() => {
+  useEffect(async () => {
     if (api && type && entity) {
-      fetchEntity(api, type, entity);
+      await fetchEntity(api, type, entity);
     }
   }, [type, entity]);
 
   return (
     <div>
-      {entityPage.failed || (!entityPage.loading && fetchNotFound(entityPage)) ? (
-        <NotFoundPage />
-      ) : entityPage.loading || checkIfEmpty(entityPage) ? (
+      {entityPage.loading ? (
         <LoadingDots />
+      ) : entityPage.failed || (!entityPage.loading && fetchNotFound(entityPage)) ? (
+        <NotFoundPage />
       ) : (
         <EntityPageView
           entity={entityPage}
diff --git a/dashboard/src/components/entitypage/EntityPageSlice.js b/dashboard/src/components/entitypage/EntityPageSlice.js
index e21df39d7b..541c1acf3e 100644
--- a/dashboard/src/components/entitypage/EntityPageSlice.js
+++ b/dashboard/src/components/entitypage/EntityPageSlice.js
@@ -28,7 +28,7 @@ const entityPageSlice = createSlice({
       if (requestId !== state.requestId) {
         return;
       }
-      state.resources = action.payload.data;
+      state.resources = action.payload?.data;
       state.loading = false;
       state.failed = false;
     },
diff --git a/nginx.conf b/nginx.conf
index 5beb7e786d..9e3e0505a1 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -33,7 +33,7 @@ http{
         location ~ ^/([^data/])?(training-sets|features|entities|labels|models|sources|providers|users)/([^/]+?)(?:/)?$ {
             try_files /[type]/[entity].html /404.html;
         }
-    
+
         location /data {
             proxy_pass  http://127.0.0.1:3001/data;
         }


Test patch:
diff --git a/dashboard/__tests__/entityPage.test.js b/dashboard/__tests__/entityPage.test.js
index 771b92a875..47c4a22e5a 100644
--- a/dashboard/__tests__/entityPage.test.js
+++ b/dashboard/__tests__/entityPage.test.js
@@ -104,4 +104,19 @@ describe("Entity Page Tests", () => {
         expect(foundPageMock).toBeDefined();
         expect(apiMock.fetchEntity).toHaveBeenCalledTimes(1);
     });
+
+    test('Issue-769: If no resource data is found, display the "404 not found" component', async () => {
+        //given: an empty resources response obj
+        const state = { ...defaultState, entityPage: { failed: false, loading: false, resources: {} } };
+        const helper = render(getTestBody(state));
+
+         //when: 
+         const notFoundDiv = await helper.findByTestId(NOT_FOUND);
+         const foundFoundElem = helper.getByText("404", { exact: false })
+ 
+         //then: 
+         expect(notFoundDiv).toBeDefined();
+         expect(foundFoundElem.nodeName).toBe("H1");
+         expect(apiMock.fetchEntity).toHaveBeenCalledTimes(1);
+    });
 });