From d921110658fd3b7ff31c621fe0aae9d153361205 Mon Sep 17 00:00:00 2001 From: Nilesh Sarupriya Date: Tue, 18 Feb 2025 15:53:15 +0530 Subject: [PATCH] chore: add postSaveHook (#39306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description > [!IMPORTANT] > Add a post save hook on datasources. Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Sanity" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: 260ad934fc1295d2ad5ed035ff593c9950158c74 > Cypress dashboard. > Tags: `@tag.Sanity` > Spec: >
Tue, 18 Feb 2025 09:36:50 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced multi-tenant support: Datasource records now incorporate tenant and instance details for seamless management across environments. - Extended post-save actions: Datasource saving triggers additional operations via plugin integrations to improve overall functionality. - New transient metadata field added to DatasourceStorage for additional data handling. - New post-save hook method introduced for plugins to perform actions after saving a datasource. - New constant `TENANT_ID` added for consistent field name referencing. --------- Co-authored-by: Nilesh Sarupriya <20905988+nsarupr@users.noreply.github.com> --- .../external/models/DatasourceStorage.java | 3 + .../external/plugins/PluginExecutor.java | 7 ++ .../server/constants/ce/FieldNameCE.java | 1 + .../base/DatasourceServiceCEImpl.java | 67 +++++++++++++------ .../base/DatasourceServiceImpl.java | 10 ++- .../base/DatasourceStorageServiceCEImpl.java | 15 ++++- 6 files changed, 79 insertions(+), 24 deletions(-) diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DatasourceStorage.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DatasourceStorage.java index a038402c7c..3a0e7898f6 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DatasourceStorage.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DatasourceStorage.java @@ -89,6 +89,9 @@ public class DatasourceStorage extends GitSyncedDomain { @Transient Boolean isMock; + @Transient + Map metadata; + public DatasourceStorage( String datasourceId, String environmentId, diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java index a95a7c2988..70acc28eeb 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java @@ -185,6 +185,13 @@ public interface PluginExecutor extends ExtensionPoint, CrudTemplateService { return Mono.just(datasourceStorage); } + /** + * This function is being called as a hook after saving a datasource. + */ + default Mono postSaveHook(DatasourceStorage datasourceStorage) { + return Mono.just(datasourceStorage); + } + /** * This function fetches the structure of the tables/collections in the datasource. It's used to make query creation * easier for the user. diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java index 2ffdc1ba5c..aeab3822f0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java @@ -197,6 +197,7 @@ public class FieldNameCE { public static final String REMOTE_PLUGINS = "remotePlugins"; public static final String INSTANCE_ID = "instanceId"; + public static final String TENANT_ID = "tenantId"; public static final String IP_ADDRESS = "ipAddress"; public static final String VERSION = "version"; public static final String PUBLISHED = "published"; 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 9d03ab90ae..3bd7a58a0c 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 @@ -28,10 +28,12 @@ import com.appsmith.server.ratelimiting.RateLimitService; import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.repositories.NewActionRepository; import com.appsmith.server.services.AnalyticsService; +import com.appsmith.server.services.ConfigService; import com.appsmith.server.services.DatasourceContextService; import com.appsmith.server.services.FeatureFlagService; import com.appsmith.server.services.SequenceService; import com.appsmith.server.services.SessionUserService; +import com.appsmith.server.services.TenantService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.EnvironmentPermission; @@ -64,6 +66,8 @@ import java.util.UUID; import static com.appsmith.external.constants.spans.DatasourceSpan.FETCH_ALL_DATASOURCES_WITH_STORAGES; import static com.appsmith.external.constants.spans.DatasourceSpan.FETCH_ALL_PLUGINS_IN_WORKSPACE; import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties; +import static com.appsmith.server.constants.ce.FieldNameCE.INSTANCE_ID; +import static com.appsmith.server.constants.ce.FieldNameCE.TENANT_ID; import static com.appsmith.server.dtos.DBOpsType.SAVE; import static com.appsmith.server.helpers.CollectionUtils.isNullOrEmpty; import static com.appsmith.server.helpers.DatasourceAnalyticsUtils.getAnalyticsProperties; @@ -93,6 +97,8 @@ public class DatasourceServiceCEImpl implements DatasourceServiceCE { private final RateLimitService rateLimitService; private final FeatureFlagService featureFlagService; private final ObservationRegistry observationRegistry; + private final TenantService tenantService; + private final ConfigService configService; // Defines blocking duration for test as well as connection created for query execution // This will block the creation of datasource connection for 5 minutes, in case of more than 3 failed connection @@ -119,7 +125,9 @@ public class DatasourceServiceCEImpl implements DatasourceServiceCE { EnvironmentPermission environmentPermission, RateLimitService rateLimitService, FeatureFlagService featureFlagService, - ObservationRegistry observationRegistry) { + ObservationRegistry observationRegistry, + TenantService tenantService, + ConfigService configService) { this.workspaceService = workspaceService; this.sessionUserService = sessionUserService; @@ -138,6 +146,8 @@ public class DatasourceServiceCEImpl implements DatasourceServiceCE { this.rateLimitService = rateLimitService; this.featureFlagService = featureFlagService; this.observationRegistry = observationRegistry; + this.tenantService = tenantService; + this.configService = configService; } @Override @@ -223,27 +233,28 @@ public class DatasourceServiceCEImpl implements DatasourceServiceCE { } return datasourceMono.flatMap(savedDatasource -> this.organiseDatasourceStorages(savedDatasource) - .flatMap(datasourceStorage -> { - // Make sure that we are creating entries only if the id is not already populated - if (hasText(datasourceStorage.getId())) { - return Mono.just(datasourceStorage); - } + .flatMap(datasourceStorageX -> setAdditionalMetadataInDatasourceStorage(datasourceStorageX) + .flatMap(datasourceStorage -> { + // Make sure that we are creating entries only if the id is not already populated + if (hasText(datasourceStorage.getId())) { + return Mono.just(datasourceStorage); + } - return datasourceStorageService - .create(datasourceStorage, isDryOps) - .map(datasourceStorage1 -> { - if (datasourceStorageDryRunQueries != null && isDryOps) { - List datasourceStorages = - datasourceStorageDryRunQueries.get(SAVE); - if (datasourceStorages == null) { - datasourceStorages = new ArrayList<>(); - } - datasourceStorages.add(datasourceStorage1); - datasourceStorageDryRunQueries.put(SAVE, datasourceStorages); - } - return datasourceStorage1; - }); - }) + return datasourceStorageService + .create(datasourceStorage, isDryOps) + .map(datasourceStorage1 -> { + if (datasourceStorageDryRunQueries != null && isDryOps) { + List datasourceStorages = + datasourceStorageDryRunQueries.get(SAVE); + if (datasourceStorages == null) { + datasourceStorages = new ArrayList<>(); + } + datasourceStorages.add(datasourceStorage1); + datasourceStorageDryRunQueries.put(SAVE, datasourceStorages); + } + return datasourceStorage1; + }); + })) .map(datasourceStorageService::createDatasourceStorageDTOFromDatasourceStorage) .collectMap(DatasourceStorageDTO::getEnvironmentId) .map(savedStorages -> { @@ -252,6 +263,20 @@ public class DatasourceServiceCEImpl implements DatasourceServiceCE { })); } + private Mono setAdditionalMetadataInDatasourceStorage(DatasourceStorage datasourceStorage) { + Mono tenantIdMono = tenantService.getDefaultTenantId(); + Mono instanceIdMono = configService.getInstanceId(); + + Map metadata = new HashMap<>(); + + return tenantIdMono.zipWith(instanceIdMono).map(tuple -> { + metadata.put(TENANT_ID, tuple.getT1()); + metadata.put(INSTANCE_ID, tuple.getT2()); + datasourceStorage.setMetadata(metadata); + return datasourceStorage; + }); + } + // this requires an EE override multiple environments protected Flux organiseDatasourceStorages(@NotNull Datasource savedDatasource) { Map storages = savedDatasource.getDatasourceStorages(); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceImpl.java index 1fc7bf66b4..67a8e8f3d9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceImpl.java @@ -8,10 +8,12 @@ import com.appsmith.server.ratelimiting.RateLimitService; import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.repositories.NewActionRepository; import com.appsmith.server.services.AnalyticsService; +import com.appsmith.server.services.ConfigService; import com.appsmith.server.services.DatasourceContextService; import com.appsmith.server.services.FeatureFlagService; import com.appsmith.server.services.SequenceService; import com.appsmith.server.services.SessionUserService; +import com.appsmith.server.services.TenantService; import com.appsmith.server.services.WorkspaceService; import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.EnvironmentPermission; @@ -41,7 +43,9 @@ public class DatasourceServiceImpl extends DatasourceServiceCEImpl implements Da EnvironmentPermission environmentPermission, RateLimitService rateLimitService, FeatureFlagService featureFlagService, - ObservationRegistry observationRegistry) { + ObservationRegistry observationRegistry, + TenantService tenantService, + ConfigService configService) { super( repository, @@ -60,6 +64,8 @@ public class DatasourceServiceImpl extends DatasourceServiceCEImpl implements Da environmentPermission, rateLimitService, featureFlagService, - observationRegistry); + observationRegistry, + tenantService, + configService); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasourcestorages/base/DatasourceStorageServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasourcestorages/base/DatasourceStorageServiceCEImpl.java index 556880330c..5b2a16b0f8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/datasourcestorages/base/DatasourceStorageServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/datasourcestorages/base/DatasourceStorageServiceCEImpl.java @@ -183,6 +183,16 @@ public class DatasourceStorageServiceCEImpl implements DatasourceStorageServiceC return pluginExecutorMono.flatMap(pluginExecutor -> pluginExecutor.preSaveHook(datasourceStorage)); } + public Mono executePostSaveActions(DatasourceStorage datasourceStorage) { + Mono pluginMono = pluginService.findById(datasourceStorage.getPluginId()); + Mono pluginExecutorMono = pluginExecutorHelper + .getPluginExecutor(pluginMono) + .switchIfEmpty(Mono.error(new AppsmithException( + AppsmithError.NO_RESOURCE_FOUND, FieldName.PLUGIN, datasourceStorage.getPluginId()))); + + return pluginExecutorMono.flatMap(pluginExecutor -> pluginExecutor.postSaveHook(datasourceStorage)); + } + @Override public Mono validateDatasourceStorage(DatasourceStorage datasourceStorage) { @@ -242,7 +252,10 @@ public class DatasourceStorageServiceCEImpl implements DatasourceStorageServiceC unsavedDatasourceStorage.updateForBulkWriteOperation(); return Mono.just(unsavedDatasourceStorage); } - return repository.save(unsavedDatasourceStorage).thenReturn(unsavedDatasourceStorage); + return repository + .save(unsavedDatasourceStorage) + .then(this.executePostSaveActions(unsavedDatasourceStorage)) + .thenReturn(unsavedDatasourceStorage); }); }