feat: clean up datasource review page (#34655)

## Description
Remove the "Generate new page" button from the datasource review page
and make the "New Query" button a primary button. Tests related to the
removed generate CRUD page feature have been removed.


Fixes #31801 

## Automation

/ok-to-test tags="@tag.Datasource"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/9772204191>
> Commit: 1d77326f4674abe6143d5c7031f91130a4bf5598
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=9772204191&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Datasource`
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a feature flag to conditionally show the `Generate Page`
button based on the drag and drop building blocks setting.
  
- **Tests**
- Added tests to verify the rendering of components and buttons based on
feature flags and plugin types.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Jacques Ikot 2024-07-03 10:41:46 +01:00 committed by GitHub
parent 9f3c5bd26d
commit 5d879ec670
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 598 additions and 20 deletions

View File

@ -1,34 +1,34 @@
import React from "react";
import { useSelector } from "react-redux";
import NewActionButton from "pages/Editor/DataSourceEditor/NewActionButton";
import { EditorNames } from "./";
import type { Datasource } from "entities/Datasource";
import type { ApiDatasourceForm } from "entities/Datasource/RestAPIForm";
import { Button } from "design-system";
import { generateTemplateFormURL } from "@appsmith/RouteBuilder";
import {
GENERATE_NEW_PAGE_BUTTON_TEXT,
createMessage,
} from "@appsmith/constants/messages";
import { ActionParentEntityType } from "@appsmith/entities/Engine/actionHelpers";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import type { AppState } from "@appsmith/reducers";
import { getPlugin } from "@appsmith/selectors/entitiesSelector";
import AnalyticsUtil from "@appsmith/utils/AnalyticsUtil";
import history from "utils/history";
import { generateTemplateFormURL } from "@appsmith/RouteBuilder";
import {
getHasCreatePagePermission,
hasCreateDSActionPermissionInApp,
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import { Button } from "design-system";
import type { Datasource } from "entities/Datasource";
import type { ApiDatasourceForm } from "entities/Datasource/RestAPIForm";
import NewActionButton from "pages/Editor/DataSourceEditor/NewActionButton";
import { useShowPageGenerationOnHeader } from "pages/Editor/DataSourceEditor/hooks";
import React from "react";
import { useSelector } from "react-redux";
import {
getCurrentApplication,
getCurrentApplicationId,
getCurrentPageId,
getPagePermissions,
} from "selectors/editorSelectors";
import { useShowPageGenerationOnHeader } from "pages/Editor/DataSourceEditor/hooks";
import type { AppState } from "@appsmith/reducers";
import {
getHasCreatePagePermission,
hasCreateDSActionPermissionInApp,
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { ActionParentEntityType } from "@appsmith/entities/Engine/actionHelpers";
import { isEnabledForPreviewData } from "utils/editorContextUtils";
import { getPlugin } from "@appsmith/selectors/entitiesSelector";
import history from "utils/history";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { EditorNames } from "./";
export interface HeaderActionProps {
datasource: Datasource | ApiDatasourceForm | undefined;
@ -48,6 +48,9 @@ export const useHeaderActions = (
) => {
const pageId = useSelector(getCurrentPageId);
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const releaseDragDropBuildingBlocks = useFeatureFlag(
FEATURE_FLAG.release_drag_drop_building_blocks_enabled,
);
const userAppPermissions = useSelector(
(state: AppState) => getCurrentApplication(state)?.userPermissions ?? [],
);
@ -65,6 +68,10 @@ export const useHeaderActions = (
const isPluginAllowedToPreviewData =
!!plugin && isEnabledForPreviewData(datasource as Datasource, plugin);
const shouldShowSecondaryGenerateButton = releaseDragDropBuildingBlocks
? false
: !!isPluginAllowedToPreviewData;
if (editorType === EditorNames.APPLICATION) {
const canCreateDatasourceActions = hasCreateDSActionPermissionInApp({
isEnabled: isFeatureEnabled,
@ -99,7 +106,7 @@ export const useHeaderActions = (
datasource={datasource as Datasource}
disabled={!canCreateDatasourceActions || !isPluginAuthorized}
eventFrom="datasource-pane"
isNewQuerySecondaryButton={!!isPluginAllowedToPreviewData}
isNewQuerySecondaryButton={shouldShowSecondaryGenerateButton}
pluginType={pluginType}
/>
);

View File

@ -77,6 +77,9 @@ const DatasourceViewModeSchema = (props: Props) => {
);
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const releaseDragDropBuildingBlocks = useFeatureFlag(
FEATURE_FLAG.release_drag_drop_building_blocks_enabled,
);
const editorType = useEditorType(history.location.pathname);
@ -230,7 +233,9 @@ const DatasourceViewModeSchema = (props: Props) => {
// if there was a failure in the fetching of the data
// if tableName from schema is availble
// if the user has permissions
// if drag and drop building blocks are not enabled
const showGeneratePageBtn =
!releaseDragDropBuildingBlocks &&
!isDatasourceStructureLoading &&
!isLoading &&
!failedFetchingPreviewData &&

View File

@ -347,6 +347,9 @@ function GoogleSheetSchema(props: Props) {
);
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const releaseDragDropBuildingBlocks = useFeatureFlag(
FEATURE_FLAG.release_drag_drop_building_blocks_enabled,
);
const editorType = useEditorType(history.location.pathname);
@ -372,6 +375,7 @@ function GoogleSheetSchema(props: Props) {
);
const showGeneratePageBtn =
!releaseDragDropBuildingBlocks &&
!isLoading &&
!isError &&
sheetData?.length &&

View File

@ -0,0 +1,562 @@
import {
DATASOURCE_GENERATE_PAGE_BUTTON,
NEW_AI_BUTTON_TEXT,
NEW_API_BUTTON_TEXT,
NEW_QUERY_BUTTON_TEXT,
createMessage,
} from "@appsmith/constants/messages";
import { getNumberOfEntitiesInCurrentPage } from "@appsmith/selectors/entitiesSelector";
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import { PluginType } from "entities/Action";
import { DatasourceConnectionMode, type Datasource } from "entities/Datasource";
import { SSLType } from "entities/Datasource/RestAPIForm";
import { unitTestBaseMockStore } from "layoutSystems/common/dropTarget/unitTestUtils";
import React from "react";
import { Provider, useSelector } from "react-redux";
import { useParams } from "react-router";
import configureStore from "redux-mock-store";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { DSFormHeader } from "../DataSourceEditor/DSFormHeader";
import DatasourceViewModeSchema from "./DatasourceViewModeSchema";
import GoogleSheetSchema from "./GoogleSheetSchema";
/* eslint-disable @typescript-eslint/no-var-requires */
const reactRouter = require("react-router");
jest.mock("utils/hooks/useFeatureFlag");
jest.mock("react-router", () => ({
...jest.requireActual("react-router"),
useParams: jest.fn(),
}));
jest.mock("react-redux", () => ({
...jest.requireActual("react-redux"),
useSelector: jest.fn(),
}));
const mockStore = configureStore([]);
const mockSetDatasourceViewModeFlag = jest.fn();
const renderBaseDatasourceComponent = () => {
render(
<Provider store={mockStore(baseStoreForSpec)}>
<DatasourceViewModeSchema
datasource={mockDatasource}
setDatasourceViewModeFlag={mockSetDatasourceViewModeFlag}
/>
</Provider>,
);
};
const renderGoogleSheetDSComponent = () => {
render(
<Provider store={mockStore(baseStoreForSpec)}>
<GoogleSheetSchema datasourceId={mockDatasource.id} />
</Provider>,
);
};
const renderDSFormHeader = () => {
render(
<Provider store={mockStore(baseStoreForSpec)}>
<DSFormHeader
canDeleteDatasource
canManageDatasource
datasource={mockDatasource}
datasourceId={mockDatasource.id}
isDeleting={false}
isNewDatasource={false}
isPluginAuthorized
pluginImage=""
pluginName=""
pluginType={PluginType.DB}
setDatasourceViewMode={() => true}
viewMode
/>
</Provider>,
);
};
const getCreateButtonText = (pluginType: PluginType) => {
switch (pluginType) {
case PluginType.DB:
case PluginType.SAAS:
return createMessage(NEW_QUERY_BUTTON_TEXT);
case PluginType.AI:
return createMessage(NEW_AI_BUTTON_TEXT);
default:
return createMessage(NEW_API_BUTTON_TEXT);
}
};
describe("DatasourceViewModeSchema Component", () => {
it("1. should not render the 'generate page' button when release_drag_drop_building_blocks_enabled is enabled", () => {
(useFeatureFlag as jest.Mock).mockReturnValue(true);
(useParams as jest.Mock).mockReturnValue({
pageId: unitTestBaseMockStore.entities.pageList.currentPageId,
});
(useSelector as jest.Mock).mockImplementation((selector) => {
if (selector === getNumberOfEntitiesInCurrentPage) {
return 0;
}
return selector(baseStoreForSpec); // Default case for other selectors
});
renderBaseDatasourceComponent();
// Check that the "generate page" button is not rendered
const generatePageButton = screen.queryByText(
createMessage(DATASOURCE_GENERATE_PAGE_BUTTON),
);
expect(generatePageButton).not.toBeInTheDocument();
});
it("2. should render new query button as primary when release_drag_drop_building_blocks_enabled is enabled", () => {
(useFeatureFlag as jest.Mock).mockReturnValue(true);
const mockHistoryPush = jest.fn();
const mockHistoryReplace = jest.fn();
const mockHistoryLocation = {
pathname: "/",
search: "",
hash: "",
state: {},
};
jest.spyOn(reactRouter, "useHistory").mockReturnValue({
push: mockHistoryPush,
replace: mockHistoryReplace,
location: mockHistoryLocation,
});
jest.spyOn(reactRouter, "useLocation").mockReturnValue(mockHistoryLocation);
renderDSFormHeader();
// Check that the "New Query" button is rendered as primary
const newQuerySpan = screen.getByText(getCreateButtonText(PluginType.DB));
const newQueryButton = newQuerySpan.closest("button");
expect(newQueryButton).toHaveAttribute("kind", "primary");
});
});
describe("GoogleSheetSchema Component", () => {
it("1. should not render the 'generate page' button when release_drag_drop_building_blocks_enabled is enabled", () => {
(useFeatureFlag as jest.Mock).mockReturnValue(true);
(useParams as jest.Mock).mockReturnValue({
pageId: unitTestBaseMockStore.entities.pageList.currentPageId,
});
(useSelector as jest.Mock).mockImplementation((selector) => {
if (selector === getNumberOfEntitiesInCurrentPage) {
return 0;
}
return selector(baseStoreForSpec); // Default case for other selectors
});
renderGoogleSheetDSComponent();
// Check that the "generate page" button is not rendered
const generatePageButton = screen.queryByText(
createMessage(DATASOURCE_GENERATE_PAGE_BUTTON),
);
expect(generatePageButton).not.toBeInTheDocument();
});
});
const mockDatasource: Datasource = {
id: "667941878b418b52eb273895",
userPermissions: [
"execute:datasources",
"delete:datasources",
"manage:datasources",
"read:datasources",
],
name: "Users",
pluginId: "656eeb1024ec7f5154c9ba00",
workspaceId: "6679402f8b418b52eb27388d",
datasourceStorages: {
unused_env: {
datasourceId: "667941878b418b52eb273895",
environmentId: "unused_env",
datasourceConfiguration: {
url: "",
connection: {
mode: DatasourceConnectionMode.READ_WRITE,
ssl: {
authType: SSLType.DEFAULT,
authTypeControl: false,
certificateFile: {} as any,
},
},
authentication: {
authenticationType: "dbAuth",
username: "users",
},
},
isConfigured: true,
isValid: true,
},
},
invalids: [],
messages: [],
isMock: true,
};
const baseStoreForSpec = {
entities: {
...unitTestBaseMockStore.entities,
plugins: {
list: [
{
id: "656eeb1024ec7f5154c9ba00",
userPermissions: [],
name: "PostgreSQL",
type: "DB",
packageName: "postgres-plugin",
iconLocation: "https://assets.appsmith.com/logo/postgresql.svg",
documentationLink:
"https://docs.appsmith.com/reference/datasources/querying-postgres#create-crud-queries",
responseType: "TABLE",
uiComponent: "DbEditorForm",
datasourceComponent: "AutoForm",
generateCRUDPageComponent: "PostgreSQL",
allowUserDatasources: true,
isRemotePlugin: false,
templates: {
CREATE:
"INSERT INTO users\n (name, gender, email)\nVALUES\n (\n {{ nameInput.text }},\n {{ genderDropdown.selectedOptionValue }},\n {{ emailInput.text }}\n );",
SELECT:
"SELECT * FROM <<your_table_name>> LIMIT 10;\n\n-- Please enter a valid table name and hit RUN",
UPDATE:
"UPDATE users\n SET status = 'APPROVED'\n WHERE id = {{ usersTable.selectedRow.id }};\n",
DELETE: "DELETE FROM users WHERE id = -1;",
},
remotePlugin: false,
new: false,
},
{
id: "656eeb1024ec7f5154c9ba01",
userPermissions: [],
name: "REST API",
type: "API",
packageName: "restapi-plugin",
iconLocation: "https://assets.appsmith.com/RestAPI.png",
uiComponent: "ApiEditorForm",
datasourceComponent: "RestAPIDatasourceForm",
allowUserDatasources: true,
isRemotePlugin: false,
templates: {},
remotePlugin: false,
new: false,
},
],
},
datasources: {
list: [
{
id: "667941878b418b52eb273895",
userPermissions: [
"execute:datasources",
"delete:datasources",
"manage:datasources",
"read:datasources",
],
name: "Users",
pluginId: "656eeb1024ec7f5154c9ba00",
workspaceId: "6679402f8b418b52eb27388d",
datasourceStorages: {
unused_env: {
id: "667941878b418b52eb273896",
datasourceId: "667941878b418b52eb273895",
environmentId: "unused_env",
datasourceConfiguration: {
connection: {
mode: "READ_WRITE",
ssl: {
authType: "DEFAULT",
},
},
endpoints: [
{
host: "mockdb.internal.appsmith.com",
},
],
authentication: {
authenticationType: "dbAuth",
username: "users",
databaseName: "users",
},
},
isConfigured: true,
invalids: [],
messages: [],
isValid: true,
},
},
invalids: [],
messages: [],
isRecentlyCreated: true,
isMock: true,
isValid: true,
new: false,
},
],
loading: false,
isTesting: false,
isListing: false,
fetchingDatasourceStructure: {
"66793e2a8b418b52eb27388a": false,
"667941878b418b52eb273895": false,
},
structure: {
"66793e2a8b418b52eb27388a": {
tables: [
{
type: "TABLE",
schema: "public",
name: "public.users",
columns: [
{
name: "id",
type: "int4",
defaultValue: "nextval('users_id_seq'::regclass)",
isAutogenerated: true,
},
{
name: "gender",
type: "text",
isAutogenerated: false,
},
{
name: "latitude",
type: "text",
isAutogenerated: false,
},
{
name: "longitude",
type: "text",
isAutogenerated: false,
},
{
name: "dob",
type: "timestamptz",
isAutogenerated: false,
},
{
name: "phone",
type: "text",
isAutogenerated: false,
},
{
name: "email",
type: "text",
isAutogenerated: false,
},
{
name: "image",
type: "text",
isAutogenerated: false,
},
{
name: "country",
type: "text",
isAutogenerated: false,
},
{
name: "name",
type: "text",
isAutogenerated: false,
},
{
name: "created_at",
type: "timestamp",
isAutogenerated: false,
},
{
name: "updated_at",
type: "timestamp",
isAutogenerated: false,
},
],
keys: [
{
name: "users_pkey",
columnNames: ["id"],
type: "primary key",
},
],
templates: [
{
title: "SELECT",
body: 'SELECT * FROM public."users" LIMIT 10;',
suggested: true,
},
{
title: "INSERT",
body: 'INSERT INTO public."users" ("gender", "latitude", "longitude", "dob", "phone", "email", "image", "country", "name", "created_at", "updated_at")\n VALUES (\'\', \'\', \'\', TIMESTAMP WITH TIME ZONE \'2019-07-01 06:30:00 CET\', \'\', \'\', \'\', \'\', \'\', TIMESTAMP \'2019-07-01 10:00:00\', TIMESTAMP \'2019-07-01 10:00:00\');',
suggested: false,
},
{
title: "UPDATE",
body: 'UPDATE public."users" SET\n "gender" = \'\',\n "latitude" = \'\',\n "longitude" = \'\',\n "dob" = TIMESTAMP WITH TIME ZONE \'2019-07-01 06:30:00 CET\',\n "phone" = \'\',\n "email" = \'\',\n "image" = \'\',\n "country" = \'\',\n "name" = \'\',\n "created_at" = TIMESTAMP \'2019-07-01 10:00:00\',\n "updated_at" = TIMESTAMP \'2019-07-01 10:00:00\'\n WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may update every row in the table!',
suggested: false,
},
{
title: "DELETE",
body: 'DELETE FROM public."users"\n WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may delete everything in the table!',
suggested: false,
},
],
},
],
},
"667941878b418b52eb273895": {
tables: [
{
type: "TABLE",
schema: "public",
name: "public.users",
columns: [
{
name: "id",
type: "int4",
defaultValue: "nextval('users_id_seq'::regclass)",
isAutogenerated: true,
},
{
name: "gender",
type: "text",
isAutogenerated: false,
},
{
name: "latitude",
type: "text",
isAutogenerated: false,
},
{
name: "longitude",
type: "text",
isAutogenerated: false,
},
{
name: "dob",
type: "timestamptz",
isAutogenerated: false,
},
{
name: "phone",
type: "text",
isAutogenerated: false,
},
{
name: "email",
type: "text",
isAutogenerated: false,
},
{
name: "image",
type: "text",
isAutogenerated: false,
},
{
name: "country",
type: "text",
isAutogenerated: false,
},
{
name: "name",
type: "text",
isAutogenerated: false,
},
{
name: "created_at",
type: "timestamp",
isAutogenerated: false,
},
{
name: "updated_at",
type: "timestamp",
isAutogenerated: false,
},
],
keys: [
{
name: "users_pkey",
columnNames: ["id"],
type: "primary key",
},
],
templates: [
{
title: "SELECT",
body: 'SELECT * FROM public."users" LIMIT 10;',
suggested: true,
},
{
title: "INSERT",
body: 'INSERT INTO public."users" ("gender", "latitude", "longitude", "dob", "phone", "email", "image", "country", "name", "created_at", "updated_at")\n VALUES (\'\', \'\', \'\', TIMESTAMP WITH TIME ZONE \'2019-07-01 06:30:00 CET\', \'\', \'\', \'\', \'\', \'\', TIMESTAMP \'2019-07-01 10:00:00\', TIMESTAMP \'2019-07-01 10:00:00\');',
suggested: false,
},
{
title: "UPDATE",
body: 'UPDATE public."users" SET\n "gender" = \'\',\n "latitude" = \'\',\n "longitude" = \'\',\n "dob" = TIMESTAMP WITH TIME ZONE \'2019-07-01 06:30:00 CET\',\n "phone" = \'\',\n "email" = \'\',\n "image" = \'\',\n "country" = \'\',\n "name" = \'\',\n "created_at" = TIMESTAMP \'2019-07-01 10:00:00\',\n "updated_at" = TIMESTAMP \'2019-07-01 10:00:00\'\n WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may update every row in the table!',
suggested: false,
},
{
title: "DELETE",
body: 'DELETE FROM public."users"\n WHERE 1 = 0; -- Specify a valid condition here. Removing the condition may delete everything in the table!',
suggested: false,
},
],
},
],
},
},
isFetchingMockDataSource: false,
mockDatasourceList: [
{
pluginType: "db",
packageName: "mongo-plugin",
description: "This contains a standard movies collection",
name: "Movies",
},
{
pluginType: "db",
packageName: "postgres-plugin",
description: "This contains a standard users information",
name: "Users",
},
],
executingDatasourceQuery: false,
isReconnectingModalOpen: false,
unconfiguredList: [],
isDatasourceBeingSaved: false,
isDatasourceBeingSavedFromPopup: false,
gsheetToken: "",
gsheetProjectID: "",
gsheetStructure: {
spreadsheets: {},
sheets: {},
columns: {},
isFetchingSpreadsheets: false,
isFetchingSheets: false,
isFetchingColumns: false,
},
recentDatasources: [],
isDeleting: false,
},
},
ui: {
...unitTestBaseMockStore.ui,
datasourcePane: {
selectedTableName: "users",
},
datasourceName: {
isSaving: [mockDatasource.id],
errors: [mockDatasource.id],
},
},
environments: {
currentEnvironmentDetails: {
id: "unused_env",
name: "",
},
},
};