diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx
index b4a6ea2adc5d..602562fc26b7 100644
--- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx
+++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx
@@ -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();
@@ -29,6 +29,9 @@ const UQIEditorForm = () => {
flexDirection="column"
w="100%"
>
+ {plugin.packageName === PluginPackageName.APPSMITH_AI && (
+
+ )}
"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";
diff --git a/app/client/src/components/editorComponents/AppsmithAIDeprecationCallout.test.tsx b/app/client/src/components/editorComponents/AppsmithAIDeprecationCallout.test.tsx
new file mode 100644
index 000000000000..a7e1988b4dc0
--- /dev/null
+++ b/app/client/src/components/editorComponents/AppsmithAIDeprecationCallout.test.tsx
@@ -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(
+
+
+
+
+ ,
+ );
+
+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);
+ });
+});
diff --git a/app/client/src/components/editorComponents/AppsmithAIDeprecationCallout.tsx b/app/client/src/components/editorComponents/AppsmithAIDeprecationCallout.tsx
new file mode 100644
index 000000000000..6d261ce99ead
--- /dev/null
+++ b/app/client/src/components/editorComponents/AppsmithAIDeprecationCallout.tsx
@@ -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 (
+
+ openDoc(DocsLink.APPSMITH_AI_DEPRECATION),
+ },
+ ]}
+ >
+ {createMessage(APPSMITH_AI_DEPRECATION_MESSAGE)}
+
+
+ );
+}
+
+export default AppsmithAIDeprecationCallout;
diff --git a/app/client/src/constants/DocumentationLinks.ts b/app/client/src/constants/DocumentationLinks.ts
index 1083c4b2a800..66a88e80ee89 100644
--- a/app/client/src/constants/DocumentationLinks.ts
+++ b/app/client/src/constants/DocumentationLinks.ts
@@ -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 = {
@@ -22,6 +23,9 @@ const LinkData: Record = {
"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) => {
diff --git a/app/client/src/pages/Editor/DataSourceEditor/index.tsx b/app/client/src/pages/Editor/DataSourceEditor/index.tsx
index a34c19573a87..90877659194b 100644
--- a/app/client/src/pages/Editor/DataSourceEditor/index.tsx
+++ b/app/client/src/pages/Editor/DataSourceEditor/index.tsx
@@ -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";
@@ -1031,6 +1032,11 @@ class DatasourceEditorRouter extends React.Component {
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 && (
+
+ )}
{
// 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[];
diff --git a/app/client/src/pages/Editor/IntegrationEditor/__tests__/aiPlugins.test.tsx b/app/client/src/pages/Editor/IntegrationEditor/__tests__/aiPlugins.test.tsx
new file mode 100644
index 000000000000..17c213db9a6e
--- /dev/null
+++ b/app/client/src/pages/Editor/IntegrationEditor/__tests__/aiPlugins.test.tsx
@@ -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(
+ {}} />,
+ );
+
+ // 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();
+ });
+});
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCE.java
index 7f88310d3185..1b810c015525 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCE.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCE.java
@@ -65,6 +65,12 @@ public interface DatasourceServiceCE {
Mono create(Datasource datasource);
+ /**
+ * Same as {@link #create(Datasource)} but skips the deprecated-plugin creation guard. Meant for internal flows
+ * (e.g. forking an application) that must keep working for datasources of deprecated plugins that already exist.
+ */
+ Mono createWithoutDeprecationCheck(Datasource datasource);
+
Mono createWithoutPermissions(Datasource datasource);
Mono createWithoutPermissions(
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java
index 5fcedc47b575..eea8ed1ec01a 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java
@@ -1,6 +1,7 @@
package com.appsmith.server.datasources.base;
import com.appsmith.external.constants.AnalyticsEvents;
+import com.appsmith.external.constants.PluginConstants;
import com.appsmith.external.enums.FeatureFlagEnum;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfiguration;
@@ -139,11 +140,40 @@ public DatasourceServiceCEImpl(
@Override
public Mono create(Datasource datasource) {
+ return validatePluginNotDeprecated(datasource)
+ .then(Mono.defer(() -> createWithoutDeprecationCheck(datasource)));
+ }
+
+ @Override
+ public Mono createWithoutDeprecationCheck(Datasource datasource) {
return workspacePermission
.getDatasourceCreatePermission()
.flatMap(permission -> createEx(datasource, permission, false, null));
}
+ /**
+ * Plugins for which creating new datasources is blocked because the plugin is deprecated.
+ * EE overrides this to extend the blocked list (e.g. appsmith-agent-plugin).
+ */
+ protected Set getDeprecatedPluginPackageNames() {
+ return Set.of(PluginConstants.PackageName.APPSMITH_AI_PLUGIN);
+ }
+
+ private Mono validatePluginNotDeprecated(Datasource datasource) {
+ // Only block brand-new datasources. Calls that carry an id are storage-saves for datasources that already
+ // exist and must keep working so users can keep using them until they migrate. A missing pluginId falls
+ // through to the INVALID_PARAMETER validation in createEx.
+ if (hasText(datasource.getId()) || !hasText(datasource.getPluginId())) {
+ return Mono.empty();
+ }
+
+ return pluginService
+ .findById(datasource.getPluginId())
+ .filter(plugin -> getDeprecatedPluginPackageNames().contains(plugin.getPackageName()))
+ .flatMap(plugin -> Mono.error(
+ new AppsmithException(AppsmithError.DEPRECATED_DATASOURCE_PLUGIN, plugin.getName())));
+ }
+
// TODO: Check usage
@Override
public Mono createWithoutPermissions(
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/fork/DatasourceForkableServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/fork/DatasourceForkableServiceCEImpl.java
index e9655431471d..64d163c00e74 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/fork/DatasourceForkableServiceCEImpl.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/fork/DatasourceForkableServiceCEImpl.java
@@ -157,13 +157,17 @@ private Mono createSuffixedDatasource(Datasource datasource) {
private Mono createSuffixedDatasource(Datasource datasource, String name, int suffix) {
final String actualName = name + (suffix == 0 ? "" : " (" + suffix + ")");
datasource.setName(actualName);
- return datasourceService.create(datasource).onErrorResume(DuplicateKeyException.class, error -> {
- if (error.getMessage() != null
- && error.getMessage().contains("workspace_datasource_deleted_compound_index")) {
- // The duplicate key error is because of the `name` field.
- return createSuffixedDatasource(datasource, name, 1 + suffix);
- }
- throw error;
- });
+ // Forking must keep working for apps that already contain datasources of deprecated plugins,
+ // so skip the deprecated-plugin creation guard here.
+ return datasourceService
+ .createWithoutDeprecationCheck(datasource)
+ .onErrorResume(DuplicateKeyException.class, error -> {
+ if (error.getMessage() != null
+ && error.getMessage().contains("workspace_datasource_deleted_compound_index")) {
+ // The duplicate key error is because of the `name` field.
+ return createSuffixedDatasource(datasource, name, 1 + suffix);
+ }
+ throw error;
+ });
}
}
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java
index 4bf2a1efa211..9dbf4c614c09 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java
@@ -516,6 +516,15 @@ public enum AppsmithError {
"Datasource cannot be deleted",
ErrorType.BAD_REQUEST,
null),
+ DEPRECATED_DATASOURCE_PLUGIN(
+ 400,
+ AppsmithErrorCode.DEPRECATED_DATASOURCE_PLUGIN.getCode(),
+ "Creating new datasources with the {0} plugin is not supported because the plugin is deprecated."
+ + " Please create an OpenAI, Anthropic, or Google AI datasource with your own API key instead.",
+ AppsmithErrorAction.DEFAULT,
+ "Plugin deprecated",
+ ErrorType.BAD_REQUEST,
+ null),
WORKSPACE_ID_NOT_GIVEN(
400,
AppsmithErrorCode.WORKSPACE_ID_NOT_GIVEN.getCode(),
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java
index 91f8a319badd..cc6fd35acc35 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java
+++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithErrorCode.java
@@ -82,6 +82,7 @@ public enum AppsmithErrorCode {
INVALID_DATASOURCE("AE-DTS-4013", "Invalid datasource"),
INVALID_DATASOURCE_CONFIGURATION("AE-DTS-4015", "Invalid datasource configuration"),
DATASOURCE_HAS_ACTIONS("AE-DTS-4030", "Datasource has actions"),
+ DEPRECATED_DATASOURCE_PLUGIN("AE-DTS-4031", "Datasource creation is blocked for a deprecated plugin"),
APPLICATION_FORKING_NOT_ALLOWED("AE-FRK-4034", "Application forking not allowed"),
INVALID_GIT_CONFIGURATION("AE-GIT-4031", "Invalid git configuration"),
INVALID_GIT_SSH_CONFIGURATION("AE-GIT-4032", "Invalid git ssh configuration"),
diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java
index 06df6669fe29..6f1c8e02b8a0 100644
--- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java
+++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java
@@ -1,5 +1,6 @@
package com.appsmith.server.services;
+import com.appsmith.external.constants.PluginConstants;
import com.appsmith.external.helpers.EncryptionHelper;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionDTO;
@@ -12,6 +13,7 @@
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.Endpoint;
import com.appsmith.external.models.OAuth2;
+import com.appsmith.external.models.PluginType;
import com.appsmith.external.models.Policy;
import com.appsmith.external.models.SSLDetails;
import com.appsmith.external.models.UploadedFile;
@@ -34,6 +36,7 @@
import com.appsmith.server.plugins.base.PluginService;
import com.appsmith.server.repositories.NewActionRepository;
import com.appsmith.server.repositories.PermissionGroupRepository;
+import com.appsmith.server.repositories.PluginRepository;
import com.appsmith.server.repositories.WorkspaceRepository;
import com.appsmith.server.solutions.ApplicationPermission;
import com.appsmith.server.solutions.EnvironmentPermission;
@@ -109,6 +112,9 @@ public class DatasourceServiceTest {
@Autowired
PermissionGroupRepository permissionGroupRepository;
+ @Autowired
+ PluginRepository pluginRepository;
+
@MockBean
PluginExecutorHelper pluginExecutorHelper;
@@ -294,6 +300,95 @@ public void createDatasourceWithId() {
.verify();
}
+ private Plugin getOrCreateAppsmithAiPlugin() {
+ Plugin aiPlugin = pluginService
+ .findByPackageName(PluginConstants.PackageName.APPSMITH_AI_PLUGIN)
+ .block();
+
+ if (aiPlugin == null) {
+ aiPlugin = new Plugin();
+ aiPlugin.setName(PluginConstants.PluginName.APPSMITH_AI_PLUGIN_NAME);
+ aiPlugin.setType(PluginType.AI);
+ aiPlugin.setPackageName(PluginConstants.PackageName.APPSMITH_AI_PLUGIN);
+ aiPlugin = pluginRepository.save(aiPlugin).block();
+ }
+
+ return aiPlugin;
+ }
+
+ private Datasource buildAppsmithAiDatasource(Plugin aiPlugin, String name) {
+ Datasource datasource = new Datasource();
+ datasource.setName(name);
+ datasource.setWorkspaceId(workspaceId);
+ datasource.setPluginId(aiPlugin.getId());
+ HashMap storages = new HashMap<>();
+ storages.put(defaultEnvironmentId, new DatasourceStorageDTO(null, defaultEnvironmentId, null));
+ datasource.setDatasourceStorages(storages);
+ return datasource;
+ }
+
+ @Test
+ @WithUserDetails(value = "api_user")
+ public void createDatasource_withDeprecatedAppsmithAiPlugin_throwsException() {
+ Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any()))
+ .thenReturn(Mono.just(new MockPluginExecutor()));
+
+ Plugin aiPlugin = getOrCreateAppsmithAiPlugin();
+ Datasource datasource = buildAppsmithAiDatasource(aiPlugin, "Deprecated Appsmith AI DS");
+
+ StepVerifier.create(datasourceService.create(datasource))
+ .expectErrorMatches(throwable -> throwable instanceof AppsmithException
+ && throwable
+ .getMessage()
+ .equals(AppsmithError.DEPRECATED_DATASOURCE_PLUGIN.getMessage(aiPlugin.getName())))
+ .verify();
+ }
+
+ @Test
+ @WithUserDetails(value = "api_user")
+ public void createWithoutDeprecationCheck_withDeprecatedAppsmithAiPlugin_succeeds() {
+ Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any()))
+ .thenReturn(Mono.just(new MockPluginExecutor()));
+
+ Plugin aiPlugin = getOrCreateAppsmithAiPlugin();
+ Datasource datasource = buildAppsmithAiDatasource(aiPlugin, "Existing Appsmith AI DS");
+
+ // This is the path fork uses; it must keep working for already-existing datasources.
+ StepVerifier.create(datasourceService.createWithoutDeprecationCheck(datasource))
+ .assertNext(createdDatasource -> {
+ assertThat(createdDatasource.getId()).isNotEmpty();
+ assertThat(createdDatasource.getPluginId()).isEqualTo(aiPlugin.getId());
+ })
+ .verifyComplete();
+ }
+
+ @Test
+ @WithUserDetails(value = "api_user")
+ public void createDatasource_existingAppsmithAiDatasource_storageSaveAllowed() {
+ Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any()))
+ .thenReturn(Mono.just(new MockPluginExecutor()));
+
+ Plugin aiPlugin = getOrCreateAppsmithAiPlugin();
+ Datasource datasource = buildAppsmithAiDatasource(aiPlugin, "Existing Appsmith AI DS for storage save");
+
+ Datasource savedDatasource =
+ datasourceService.createWithoutDeprecationCheck(datasource).block();
+ assertThat(savedDatasource).isNotNull();
+ assertThat(savedDatasource.getId()).isNotEmpty();
+
+ // Calls that carry an id are storage-saves for existing datasources and must bypass the deprecation guard.
+ Datasource existingDatasource = new Datasource();
+ existingDatasource.setId(savedDatasource.getId());
+ existingDatasource.setWorkspaceId(workspaceId);
+ existingDatasource.setPluginId(aiPlugin.getId());
+ existingDatasource.setDatasourceStorages(new HashMap<>(savedDatasource.getDatasourceStorages()));
+
+ StepVerifier.create(datasourceService.create(existingDatasource))
+ .assertNext(
+ resultDatasource -> assertThat(resultDatasource.getId()).isEqualTo(savedDatasource.getId()))
+ .verifyComplete();
+ }
+
@Test
@WithUserDetails(value = "api_user")
public void createDatasourceNotInstalledPlugin() {