You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2024/05/22 00:40:57 UTC

(superset) 01/01: add refresh for thumbnail_url

This is an automated email from the ASF dual-hosted git repository.

elizabeth pushed a commit to branch fix/dashboard-perf
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 49bcc719da06a78a306ed429f54197c21dd546ff
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Tue May 21 15:49:04 2024 -0700

    add refresh for thumbnail_url
---
 .../src/features/dashboards/DashboardCard.test.tsx | 139 +++++++++++++++++++++
 .../src/features/dashboards/DashboardCard.tsx      |  45 ++++++-
 2 files changed, 182 insertions(+), 2 deletions(-)

diff --git a/superset-frontend/src/features/dashboards/DashboardCard.test.tsx b/superset-frontend/src/features/dashboards/DashboardCard.test.tsx
new file mode 100644
index 0000000000..10c31fe231
--- /dev/null
+++ b/superset-frontend/src/features/dashboards/DashboardCard.test.tsx
@@ -0,0 +1,139 @@
+import React from 'react';
+import { MemoryRouter } from 'react-router-dom';
+import { FeatureFlag, SupersetClient } from '@superset-ui/core';
+import * as uiCore from '@superset-ui/core';
+
+import { render, screen, waitFor } from 'spec/helpers/testing-library';
+
+import DashboardCard from './DashboardCard';
+
+const mockDashboard = {
+  id: 1,
+  dashboard_title: 'Sample Dashboard',
+  certified_by: 'John Doe',
+  certification_details: 'Certified on 2022-01-01',
+  published: true,
+  url: '/dashboard/1',
+  thumbnail_url: '/thumbnails/1.png',
+  changed_on_delta_humanized: '2 days ago',
+  owners: [
+    { id: 1, name: 'Alice', first_name: 'Alice', last_name: 'Doe' },
+    { id: 2, name: 'Bob', first_name: 'Bob', last_name: 'Smith' },
+  ],
+  changed_by_name: 'John Doe',
+  changed_by: 'john.doe@example.com',
+};
+
+const mockHasPerm = jest.fn().mockReturnValue(true);
+const mockOpenDashboardEditModal = jest.fn();
+const mockSaveFavoriteStatus = jest.fn();
+const mockHandleBulkDashboardExport = jest.fn();
+const mockOnDelete = jest.fn();
+
+let isFeatureEnabledMock: jest.MockInstance<boolean, [feature: FeatureFlag]>;
+
+beforeAll(() => {
+  isFeatureEnabledMock = jest
+    .spyOn(uiCore, 'isFeatureEnabled')
+    .mockImplementation(() => true);
+});
+
+afterAll(() => {
+  // @ts-ignore
+  isFeatureEnabledMock.mockClear();
+});
+
+beforeEach(() => {
+  render(
+    <MemoryRouter>
+      <DashboardCard
+        dashboard={mockDashboard}
+        hasPerm={mockHasPerm}
+        bulkSelectEnabled={false}
+        loading={false}
+        openDashboardEditModal={mockOpenDashboardEditModal}
+        saveFavoriteStatus={mockSaveFavoriteStatus}
+        favoriteStatus={false}
+        handleBulkDashboardExport={mockHandleBulkDashboardExport}
+        onDelete={mockOnDelete}
+      />
+    </MemoryRouter>,
+  );
+});
+
+it('Renders the dashboard title', () => {
+  const titleElement = screen.getByText('Sample Dashboard');
+  expect(titleElement).toBeInTheDocument();
+});
+
+it('Renders the certification details', () => {
+  const certificationDetailsElement = screen.getByLabelText(/certified/i);
+  expect(certificationDetailsElement).toBeInTheDocument();
+});
+
+it('Renders the published status', () => {
+  const publishedElement = screen.getByText(/published/i);
+  expect(publishedElement).toBeInTheDocument();
+});
+
+it('Renders the modified date', () => {
+  const modifiedDateElement = screen.getByText('Modified 2 days ago');
+  expect(modifiedDateElement).toBeInTheDocument();
+});
+
+it('should fetch thumbnail when dashboard has no thumbnail URL and feature flag is enabled', async () => {
+  const mockGet = jest.spyOn(SupersetClient, 'get').mockResolvedValue({
+    response: new Response(
+      JSON.stringify({ thumbnail_url: '/new-thumbnail.png' }),
+    ),
+    json: () => Promise.resolve({ thumbnail_url: '/new-thumbnail.png' }),
+  });
+  const { rerender } = render(
+    <DashboardCard
+      dashboard={{
+        id: 1,
+        thumbnail_url: '',
+        changed_by_name: '',
+        changed_by: '',
+        dashboard_title: '',
+        published: false,
+        url: '',
+        owners: [],
+      }}
+      hasPerm={() => true}
+      bulkSelectEnabled={false}
+      loading={false}
+      saveFavoriteStatus={() => {}}
+      favoriteStatus={false}
+      handleBulkDashboardExport={() => {}}
+      onDelete={() => {}}
+    />,
+  );
+  await waitFor(() => {
+    expect(mockGet).toHaveBeenCalledWith({
+      endpoint: '/api/v1/dashboard/1',
+    });
+  });
+  rerender(
+    <DashboardCard
+      dashboard={{
+        id: 1,
+        thumbnail_url: '/new-thumbnail.png',
+        changed_by_name: '',
+        changed_by: '',
+        dashboard_title: '',
+        published: false,
+        url: '',
+        owners: [],
+      }}
+      hasPerm={() => true}
+      bulkSelectEnabled={false}
+      loading={false}
+      saveFavoriteStatus={() => {}}
+      favoriteStatus={false}
+      handleBulkDashboardExport={() => {}}
+      onDelete={() => {}}
+    />,
+  );
+  mockGet.mockRestore();
+});
diff --git a/superset-frontend/src/features/dashboards/DashboardCard.tsx b/superset-frontend/src/features/dashboards/DashboardCard.tsx
index e977c62f52..400e358ab2 100644
--- a/superset-frontend/src/features/dashboards/DashboardCard.tsx
+++ b/superset-frontend/src/features/dashboards/DashboardCard.tsx
@@ -16,9 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React from 'react';
+import React, { useEffect, useState } from 'react';
 import { Link, useHistory } from 'react-router-dom';
-import { isFeatureEnabled, FeatureFlag, t, useTheme } from '@superset-ui/core';
+import {
+  isFeatureEnabled,
+  FeatureFlag,
+  t,
+  useTheme,
+  SupersetClient,
+} from '@superset-ui/core';
 import { CardStyles } from 'src/views/CRUD/utils';
 import { AntdDropdown } from 'src/components';
 import { Menu } from 'src/components/Menu';
@@ -62,6 +68,41 @@ function DashboardCard({
   const canExport = hasPerm('can_export');
 
   const theme = useTheme();
+
+  const [thumbnailUrl, setThumbnailUrl] = useState<string | null>(null);
+  const [fetchingThumbnail, setFetchingThumbnail] = useState<boolean>(false);
+
+  useEffect(() => {
+    // fetch thumbnail only if it's not already fetched
+    console.error(
+      isFeatureEnabled(FeatureFlag.Thumbnails),
+      thumbnailUrl,
+      fetchingThumbnail,
+      dashboard.id,
+    );
+    if (
+      !fetchingThumbnail &&
+      dashboard.id &&
+      (thumbnailUrl === undefined || thumbnailUrl === null) &&
+      isFeatureEnabled(FeatureFlag.Thumbnails)
+    ) {
+      // fetch thumbnail
+      if (dashboard.thumbnail_url) {
+        // set to empty string if null so that we don't
+        // keep fetching the thumbnail
+        setThumbnailUrl(dashboard.thumbnail_url || '');
+        return;
+      }
+      setFetchingThumbnail(true);
+      SupersetClient.get({
+        endpoint: `/api/v1/dashboard/${dashboard.id}`,
+      }).then(({ json = {} }) => {
+        setThumbnailUrl(json.thumbnail_url || '');
+        setFetchingThumbnail(false);
+      });
+    }
+  }, [dashboard, thumbnailUrl]);
+
   const menu = (
     <Menu>
       {canEdit && openDashboardEditModal && (