Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { usePluginActionContext } from "../../../../PluginActionContext";
import { QUERY_EDITOR_FORM_NAME } from "ee/constants/forms";
import { reduxForm } from "redux-form";
import { Flex } from "@appsmith/ads";
import { PluginPackageName } from "entities/Plugin";
import AppsmithAIDeprecationCallout from "components/editorComponents/AppsmithAIDeprecationCallout";
import { useGoogleSheetsSetDefaultProperty } from "./hooks/useGoogleSheetsSetDefaultProperty";
import { useFormData } from "./hooks/useFormData";

const UQIEditorForm = () => {
const {
editorConfig,
plugin: { uiComponent },
} = usePluginActionContext();
const { editorConfig, plugin } = usePluginActionContext();
const { uiComponent } = plugin;

// Set default values for Google Sheets
useGoogleSheetsSetDefaultProperty();
Expand All @@ -29,6 +29,9 @@ const UQIEditorForm = () => {
flexDirection="column"
w="100%"
>
{plugin.packageName === PluginPackageName.APPSMITH_AI && (
<AppsmithAIDeprecationCallout />
)}
<FormRender
editorConfig={editorConfig}
formData={data}
Expand Down
5 changes: 5 additions & 0 deletions app/client/src/ce/constants/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@ export const CREATE_NEW_DATASOURCE_GRAPHQL_API = () => "GraphQL API";
export const CREATE_NEW_API_SECTION_HEADER = () => "APIs";
export const CREATE_NEW_SAAS_SECTION_HEADER = () => "SaaS Integrations";
export const CREATE_NEW_AI_SECTION_HEADER = () => "AI Integrations";
// Appsmith AI deprecation (release 2.3): update this date if the shutdown date moves
export const APPSMITH_AI_KILL_DATE = "September 30, 2026";
export const APPSMITH_AI_DEPRECATION_MESSAGE = () =>
`Appsmith AI is deprecated and will stop working on ${APPSMITH_AI_KILL_DATE}. Migrate to OpenAI, Anthropic, or Google AI with your own API key. Uploaded files (file context) will not be available after this date.`;
export const APPSMITH_AI_DEPRECATION_LEARN_MORE = () => "Learn how to migrate";
export const CONNECT_A_DATASOURCE_HEADING = () => "Connect a datasource";
export const CONNECT_A_DATASOURCE_SUBHEADING = () =>
"Select a sample datasource or connect your own";
Expand Down
21 changes: 12 additions & 9 deletions app/client/src/ce/hooks/datasourceEditorHooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Button } from "@appsmith/ads";
import type { Datasource } from "entities/Datasource";
import type { ApiDatasourceForm } from "entities/Datasource/RestAPIForm";
import NewActionButton from "pages/Editor/DataSourceEditor/NewActionButton";
import { PluginPackageName } from "entities/Plugin";
import { useShowPageGenerationOnHeader } from "pages/Editor/DataSourceEditor/hooks";
import React from "react";
import { useDispatch, useSelector } from "react-redux";
Expand Down Expand Up @@ -100,15 +101,17 @@ export const useHeaderActions = (
);
};

const newActionButton = (
<NewActionButton
datasource={datasource as Datasource}
disabled={!canCreateDatasourceActions || !isPluginAuthorized}
eventFrom="datasource-pane"
isNewQuerySecondaryButton={shouldShowSecondaryGenerateButton}
pluginType={pluginType}
/>
);
// Appsmith AI is deprecated: creating new queries on its datasources is blocked
const newActionButton =
plugin?.packageName === PluginPackageName.APPSMITH_AI ? null : (
<NewActionButton
datasource={datasource as Datasource}
disabled={!canCreateDatasourceActions || !isPluginAuthorized}
eventFrom="datasource-pane"
isNewQuerySecondaryButton={shouldShowSecondaryGenerateButton}
pluginType={pluginType}
/>
);

const generatePageButton =
showGenerateButton && !showReconnectButton && !isAnvilEnabled ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { ThemeProvider } from "styled-components";
import { BrowserRouter as Router } from "react-router-dom";
import { lightTheme } from "selectors/themeSelectors";
import AppsmithAIDeprecationCallout from "./AppsmithAIDeprecationCallout";
import { DocsLink, openDoc } from "constants/DocumentationLinks";
import {
APPSMITH_AI_DEPRECATION_LEARN_MORE,
APPSMITH_AI_KILL_DATE,
createMessage,
} from "ee/constants/messages";

jest.mock("constants/DocumentationLinks", () => ({
...jest.requireActual("constants/DocumentationLinks"),
openDoc: jest.fn(),
}));

const renderCallout = () =>
render(
<ThemeProvider theme={lightTheme}>
<Router>
<AppsmithAIDeprecationCallout />
</Router>
</ThemeProvider>,
);

describe("AppsmithAIDeprecationCallout", () => {
it("renders the deprecation message with the kill date", () => {
renderCallout();

expect(
screen.getByTestId("t--appsmith-ai-deprecation-callout"),
).toBeInTheDocument();
expect(screen.getByText(/Appsmith AI is deprecated/i)).toBeInTheDocument();
expect(
screen.getByText(new RegExp(APPSMITH_AI_KILL_DATE)),
).toBeInTheDocument();
});

it("renders the migration docs link", () => {
renderCallout();

expect(
screen.getByText(createMessage(APPSMITH_AI_DEPRECATION_LEARN_MORE)),
).toBeInTheDocument();
});

it("opens the deprecation docs when the link is clicked", () => {
renderCallout();

fireEvent.click(
screen.getByText(createMessage(APPSMITH_AI_DEPRECATION_LEARN_MORE)),
);

expect(openDoc).toHaveBeenCalledWith(DocsLink.APPSMITH_AI_DEPRECATION);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";
import styled from "styled-components";
import { Callout } from "@appsmith/ads";
import { DocsLink, openDoc } from "constants/DocumentationLinks";
import {
APPSMITH_AI_DEPRECATION_LEARN_MORE,
APPSMITH_AI_DEPRECATION_MESSAGE,
createMessage,
} from "ee/constants/messages";

const CalloutWrapper = styled.div`
width: 100%;
margin-bottom: var(--ads-v2-spaces-4);
`;

/**
* Deprecation notice for the managed Appsmith AI plugin (release 2.3).
* Shown on existing Appsmith AI datasources and queries; creating new
* Appsmith AI datasources is blocked separately (client filter + server guard).
*/
function AppsmithAIDeprecationCallout() {
return (
<CalloutWrapper data-testid="t--appsmith-ai-deprecation-callout">
<Callout
kind="warning"
links={[
{
children: createMessage(APPSMITH_AI_DEPRECATION_LEARN_MORE),
onClick: () => openDoc(DocsLink.APPSMITH_AI_DEPRECATION),
},
]}
>
{createMessage(APPSMITH_AI_DEPRECATION_MESSAGE)}
</Callout>
</CalloutWrapper>
);
}

export default AppsmithAIDeprecationCallout;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useFilteredAndSortedFileOperations } from "./GlobalSearchHooks";
import type { Datasource } from "entities/Datasource";
import { type Plugin, PluginPackageName } from "entities/Plugin";
import { SEARCH_ITEM_TYPES } from "./utils";

describe("getFilteredAndSortedFileOperations", () => {
Expand Down Expand Up @@ -127,6 +128,53 @@ describe("getFilteredAndSortedFileOperations", () => {
);
});

it("does not offer query creation on deprecated Appsmith AI datasources", () => {
const appsmithAiPlugin = {
id: "appsmith-ai-plugin-id",
packageName: PluginPackageName.APPSMITH_AI,
} as Plugin;

const makeDatasource = (name: string, pluginId: string): Datasource => ({
datasourceStorages: {},
id: `${name}-id`,
pluginId,
workspaceId: "",
name,
});

const fileOptions = useFilteredAndSortedFileOperations({
query: "",
allDatasources: [
makeDatasource("Appsmith AI datasource", appsmithAiPlugin.id),
makeDatasource("Postgres datasource", "postgres-plugin-id"),
],
plugins: [appsmithAiPlugin],
recentlyUsedDSMap: {},
canCreateActions: true,
canCreateDatasource: true,
});

const titles = fileOptions.map((option) => option.title);

expect(titles).toContain("New Postgres datasource query");
expect(titles).not.toContain("New Appsmith AI datasource query");

const aiOnlyFileOptions = useFilteredAndSortedFileOperations({
query: "",
allDatasources: [
makeDatasource("Appsmith AI datasource", appsmithAiPlugin.id),
],
plugins: [appsmithAiPlugin],
recentlyUsedDSMap: {},
canCreateActions: true,
canCreateDatasource: true,
});

expect(aiOnlyFileOptions.map((option) => option.title)).not.toContain(
"Create a query",
);
});

it("sorts datasources based on recency", () => {
const appDatasource: Datasource = {
datasourceStorages: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,23 @@ export const useFilteredAndSortedFileOperations = ({
moduleOptions.map((moduleOp) => fileOperations.push(moduleOp));
}

// Appsmith AI is deprecated: creating new queries on its datasources is blocked
const pluginsById = keyBy(plugins, "id");
const queryableDatasources = allDatasources.filter(
(ds) =>
pluginsById[ds.pluginId]?.packageName !== PluginPackageName.APPSMITH_AI,
);

// Add app datasources
if (allDatasources.length > 0) {
if (queryableDatasources.length > 0) {
fileOperations.push(createQueryOption);
}

// Sort datasources based on recency
const datasources = getSortedDatasources(allDatasources, recentlyUsedDSMap);
const datasources = getSortedDatasources(
queryableDatasources,
recentlyUsedDSMap,
);

const createQueryAction =
(dsId: string) =>
Expand Down
4 changes: 4 additions & 0 deletions app/client/src/constants/DocumentationLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum DocsLink {
QUERY = "QUERY",
TROUBLESHOOT_ERROR = "TROUBLESHOOT_ERROR",
QUERY_SETTINGS = "QUERY_SETTINGS",
APPSMITH_AI_DEPRECATION = "APPSMITH_AI_DEPRECATION",
}

const LinkData: Record<DocsLink, string> = {
Expand All @@ -22,6 +23,9 @@ const LinkData: Record<DocsLink, string> = {
"https://docs.appsmith.com/help-and-support/troubleshooting-guide",
QUERY_SETTINGS:
"https://docs.appsmith.com/connect-data/reference/query-settings",
// This URL hosts the Appsmith AI → BYOK migration guide (appsmith-docs#3025)
APPSMITH_AI_DEPRECATION:
"https://docs.appsmith.com/connect-data/reference/appsmith-ai",
};

export const openDoc = (type: DocsLink, link?: string, subType?: string) => {
Expand Down
6 changes: 6 additions & 0 deletions app/client/src/pages/Editor/DataSourceEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import { formValuesToDatasource } from "PluginActionEditor/transformers/RestAPID
import { DSFormHeader } from "./DSFormHeader";
import type { PluginType } from "entities/Plugin";
import { DatasourceComponentTypes, PluginPackageName } from "entities/Plugin";
import AppsmithAIDeprecationCallout from "components/editorComponents/AppsmithAIDeprecationCallout";
import DSDataFilter from "ee/components/DSDataFilter";
import { DEFAULT_ENV_ID } from "ee/api/ApiUtils";
import { isStorageEnvironmentCreated } from "ee/utils/Environments";
Expand Down Expand Up @@ -1031,6 +1032,11 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
showingTabsOnViewMode && "db-form-resizer-content-show-tabs"
}`}
>
{/* Mounted above DSEditorWrapper: the wrapper is a row flex, so a
child callout would render as a side column instead of a banner */}
{pluginPackageName === PluginPackageName.APPSMITH_AI && (
<AppsmithAIDeprecationCallout />
)}
<DSEditorWrapper
className={!!isOnboardingFlow ? "onboarding-flow" : ""}
isCreatingAiAgent={
Expand Down
9 changes: 7 additions & 2 deletions app/client/src/pages/Editor/IntegrationEditor/AIPlugins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { connect } from "react-redux";
import { createTempDatasourceFromForm } from "actions/datasourceActions";
import type { DefaultRootState } from "react-redux";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { type Plugin, PluginType } from "entities/Plugin";
import { type Plugin, PluginPackageName, PluginType } from "entities/Plugin";
import { getAssetUrl, isAirgapped } from "ee/utils/airgapHelpers";
import {
DatasourceContainer,
Expand Down Expand Up @@ -93,7 +93,12 @@ const mapStateToProps = (state: DefaultRootState) => {
// Sort the AI plugins alphabetically
return a.name.localeCompare(b.name);
})
.filter((plugin) => plugin.type === PluginType.AI),
.filter(
(plugin) =>
plugin.type === PluginType.AI &&
// Appsmith AI is deprecated — creating new datasources for it is blocked
plugin.packageName !== PluginPackageName.APPSMITH_AI,
),
searchedPlugin,
) as Plugin[];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getGenerateCRUDEnabledPluginMap } from "ee/selectors/entitiesSelector";
import {
type GenerateCRUDEnabledPluginMap,
type Plugin,
PluginPackageName,
PluginType,
} from "entities/Plugin";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
Expand Down Expand Up @@ -298,9 +299,10 @@ function DatasourceCard(props: DatasourceCardProps) {
(envSupportedDs ? isEnvironmentConfigured(datasource, currentEnv) : true)
);

const showCreateNewActionButton = envSupportedDs
? doesAnyDsConfigExist(datasource)
: true;
// Appsmith AI is deprecated: creating new queries on its datasources is blocked
const showCreateNewActionButton =
plugin.packageName !== PluginPackageName.APPSMITH_AI &&
(envSupportedDs ? doesAnyDsConfigExist(datasource) : true);

return (
<Wrapper
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from "react";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import store from "store";
import { render } from "test/testUtils";
import { PluginPackageName } from "entities/Plugin";
import AIPlugins from "../AIPlugins";

const aiPlugins = [
{
id: "appsmith-ai-plugin-id",
name: "Appsmith AI",
type: "AI",
packageName: PluginPackageName.APPSMITH_AI,
iconLocation: "",
},
{
id: "openai-plugin-id",
name: "OpenAI",
type: "AI",
packageName: "openai-plugin",
iconLocation: "",
},
];

describe("AIPlugins create-new list", () => {
it("hides deprecated Appsmith AI but keeps other AI plugins", () => {
store.dispatch({
type: ReduxActionTypes.FETCH_PLUGINS_SUCCESS,
payload: aiPlugins,
});

const { container } = render(
<AIPlugins pageId="pageId" showUnsupportedPluginDialog={() => {}} />,
);

// BYOK AI plugins must remain creatable
expect(
container.querySelector(".t--createBlankApi-openai-plugin"),
).not.toBeNull();
// The deprecated managed Appsmith AI plugin must not be offered for creation
expect(
container.querySelector(
`.t--createBlankApi-${PluginPackageName.APPSMITH_AI}`,
),
).toBeNull();
});
});
Loading
Loading