Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add postSaveHook #39306

Merged
merged 2 commits into from
Feb 18, 2025
Merged

chore: add postSaveHook #39306

merged 2 commits into from
Feb 18, 2025

Conversation

nsarupr
Copy link
Contributor

@nsarupr nsarupr commented Feb 17, 2025

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"

🔍 Cypress test results

Tip

🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/13386879786
Commit: 260ad93
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.

Copy link
Contributor

coderabbitai bot commented Feb 17, 2025

Walkthrough

This pull request introduces several enhancements centered on datasource management. A new transient field, metadata, has been added to the DatasourceStorage class. Additionally, a new default method postSaveHook is provided in the PluginExecutor interface to facilitate post-save processing. Both DatasourceServiceCEImpl and DatasourceServiceImpl classes have been updated to include TenantService and ConfigService in their constructors, enabling better management of tenant-specific configurations during datasource creation. Finally, the DatasourceStorageServiceCEImpl now executes post-save actions as part of the save workflow.

Changes

File(s) Change Summary
app/server/.../DatasourceStorage.java Added transient field Map<String, Object> metadata to hold non-persistent data in DatasourceStorage.
app/server/.../PluginExecutor.java Added a default method postSaveHook(DatasourceStorage datasourceStorage) that returns a Mono<DatasourceStorage>.
app/server/.../DatasourceServiceCEImpl.java, app/server/.../DatasourceServiceImpl.java Updated constructors to include TenantService and ConfigService dependencies; modified logic to set tenant and instance IDs on datasource objects.
app/server/.../DatasourceStorageServiceCEImpl.java Added executePostSaveActions method to invoke the post-save hook after saving datasource storage.
app/server/.../FieldNameCE.java Added constant TENANT_ID with the value "tenantId" for consistent field name referencing.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant DSStorageService as DatasourceStorageServiceCEImpl
    participant Repo as DatasourceRepository
    participant PluginSvc as PluginService
    participant ExecutorHelper as PluginExecutorHelper
    participant Executor as PluginExecutor
    DSStorageService->>Repo: Save DatasourceStorage
    DSStorageService->>DSStorageService: executePostSaveActions(datasourceStorage)
    DSStorageService->>PluginSvc: Retrieve Plugin
    DSStorageService->>ExecutorHelper: Get PluginExecutor
    ExecutorHelper->>Executor: Call postSaveHook(datasourceStorage)
    Executor-->>DSStorageService: Return Mono<DatasourceStorage>
    DSStorageService-->>Repo: Return saved & processed DatasourceStorage
Loading
sequenceDiagram
    autonumber
    participant DSService as DatasourceServiceCEImpl
    participant Tenant as TenantService
    participant Config as ConfigService
    participant DSRepo as DatasourceRepository
    DSService->>Tenant: Fetch default tenantId
    DSService->>Config: Fetch instanceId
    Tenant-->>DSService: tenantId
    Config-->>DSService: instanceId
    DSService->>DSRepo: Save DatasourceStorage with tenant & instance IDs
    DSRepo-->>DSService: Return saved DatasourceStorage
Loading

Suggested labels

Enhancement, Task, IDE Product

Suggested reviewers

  • sagar-qa007
  • abhvsn
  • ApekshaBhosale

Poem

In our code, new paths arise,
A metadata field in a clever disguise.
Post-save hooks weave their magic tight,
Tenant and instance, shining so bright.
With each commit, our data takes flight,
Celebrating changes, oh what a delight!
🚀 Happy merging under the starlit night!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the skip-changelog Adding this label to a PR prevents it from being listed in the changelog label Feb 17, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java (2)

126-128: Constructor may be getting crowded
Adding TenantService and ConfigService is valid, yet the constructor is quite large. Consider extracting some logic into dedicated service classes/factories to keep the constructor more manageable.


239-274: Flow for creating datasource storage with tenant and instance
The zipped flow correctly merges datasourceMono with tenantIdAndInstanceIdMonoCached. However, watch out for:

  • Error handling: If tenant/instance fetch fails, does the user receive clear feedback?
  • Transient fields: tenantId/instanceId are transient. Ensure usage is clarified so new contributors know these fields are not persisted.
  • Complex method: This method is fairly long. Consider extracting the inner callbacks into smaller methods to improve readability.
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd519bb and b1155a3.

📒 Files selected for processing (5)
  • app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DatasourceStorage.java (1 hunks)
  • app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java (6 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceImpl.java (3 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/datasourcestorages/base/DatasourceStorageServiceCEImpl.java (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: server-unit-tests / server-unit-tests
  • GitHub Check: server-spotless / spotless-check
🔇 Additional comments (5)
app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceImpl.java (1)

29-70:

❓ Verification inconclusive

LGTM! Clean constructor extension.

The addition of TenantService and ConfigService follows Spring's dependency injection pattern and is properly passed to the parent class.

Let's verify the parent class implementation:


🏁 Script executed:

#!/bin/bash
# Check if the parent class DatasourceServiceCEImpl has matching constructor parameters
ast-grep --pattern 'class DatasourceServiceCEImpl {
  $$$
  DatasourceServiceCEImpl($$$, TenantService $_, ConfigService $_) {
    $$$
  }
  $$$
}'

Length of output: 149


Constructor changes approved—but please manually verify the parent class signature.

The changes in DatasourceServiceImpl correctly add TenantService and ConfigService following our dependency injection pattern and are properly passed to the superclass. However, our automated search for the constructor in the parent class (DatasourceServiceCEImpl) didn’t produce a match, so please double-check that its constructor supports these new parameters to ensure consistency at runtime.

app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java (2)

31-36: Imports look consistent with usage
Both ConfigService and TenantService are referenced in the updated code. These additions are valid and appear necessary for multi-tenant handling.


193-198: Ensure .cache() usage is intentional
.cache() will indefinitely store and reuse the same tenant/instance data for every subscriber, including any errors. Verify that the tenant ID and instance ID remain constant once resolved and that caching errors is the desired behavior.

app/server/appsmith-interfaces/src/main/java/com/appsmith/external/plugins/PluginExecutor.java (1)

188-193: LGTM!

The postSaveHook method follows the established pattern of hook methods in the interface, with proper documentation and default implementation.

app/server/appsmith-server/src/main/java/com/appsmith/server/datasourcestorages/base/DatasourceStorageServiceCEImpl.java (1)

186-194: LGTM!

The executePostSaveActions method follows the established pattern of the executePreSaveActions method, with proper error handling and plugin executor retrieval.

Comment on lines 92 to 96
@Transient
String tenantId;

@Transient
String instanceId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update prepareTransientFields to include new fields.

The new transient fields tenantId and instanceId should be initialized in the prepareTransientFields method to maintain consistency with other transient fields.

Apply this diff to update the method:

 public void prepareTransientFields(Datasource datasource) {
     this.datasourceId = datasource.getId();
     this.name = datasource.getName();
     this.pluginId = datasource.getPluginId();
     this.pluginName = datasource.getPluginName();
     this.workspaceId = datasource.getWorkspaceId();
     this.templateName = datasource.getTemplateName();
     this.isAutoGenerated = datasource.getIsAutoGenerated();
     this.isRecentlyCreated = datasource.getIsRecentlyCreated();
     this.isTemplate = datasource.getIsTemplate();
     this.isMock = datasource.getIsMock();
+    this.tenantId = datasource.getTenantId();
+    this.instanceId = datasource.getInstanceId();

     if (datasource.getInvalids() != null) {
         this.invalids.addAll(datasource.getInvalids());
     }
     this.gitSyncId = datasource.getGitSyncId();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Transient
String tenantId;
@Transient
String instanceId;
public void prepareTransientFields(Datasource datasource) {
this.datasourceId = datasource.getId();
this.name = datasource.getName();
this.pluginId = datasource.getPluginId();
this.pluginName = datasource.getPluginName();
this.workspaceId = datasource.getWorkspaceId();
this.templateName = datasource.getTemplateName();
this.isAutoGenerated = datasource.getIsAutoGenerated();
this.isRecentlyCreated = datasource.getIsRecentlyCreated();
this.isTemplate = datasource.getIsTemplate();
this.isMock = datasource.getIsMock();
this.tenantId = datasource.getTenantId();
this.instanceId = datasource.getInstanceId();
if (datasource.getInvalids() != null) {
this.invalids.addAll(datasource.getInvalids());
}
this.gitSyncId = datasource.getGitSyncId();
}

Comment on lines +255 to +258
return repository
.save(unsavedDatasourceStorage)
.then(this.executePostSaveActions(unsavedDatasourceStorage))
.thenReturn(unsavedDatasourceStorage);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider transaction boundary for save operation.

The save operation and post-save actions should be executed within a transaction to ensure atomicity. If the post-save action fails, the save should be rolled back.

Consider using Spring's @Transactional annotation or a transaction template to wrap both operations:

-return repository
-        .save(unsavedDatasourceStorage)
-        .then(this.executePostSaveActions(unsavedDatasourceStorage))
-        .thenReturn(unsavedDatasourceStorage);
+return transactionTemplate.execute(status -> {
+    return repository
+            .save(unsavedDatasourceStorage)
+            .then(this.executePostSaveActions(unsavedDatasourceStorage))
+            .thenReturn(unsavedDatasourceStorage);
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return repository
.save(unsavedDatasourceStorage)
.then(this.executePostSaveActions(unsavedDatasourceStorage))
.thenReturn(unsavedDatasourceStorage);
return transactionTemplate.execute(status -> {
return repository
.save(unsavedDatasourceStorage)
.then(this.executePostSaveActions(unsavedDatasourceStorage))
.thenReturn(unsavedDatasourceStorage);
});

@nsarupr nsarupr added the ok-to-test Required label for CI label Feb 17, 2025
@nsarupr nsarupr requested a review from mohanarpit February 17, 2025 09:54
Comment on lines 92 to 96
@Transient
String tenantId;

@Transient
String instanceId;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if DatasourceStorage should have these fields configured in the POJO. I understand that we need the fields instanceId and tenantId in our operations. But having these fields in the DatasourceStorage object seems awkward as it's violating the boundaries of our system architecture.

What do you think of instead introducing a map of parameters that can store this metadata that is parsed by the plugin itself? Would that be more extendable? Or do you think it'll lead to confusion and lack of clarity in the code?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java (1)

266-278: Add logging for metadata operations.

The new method for setting metadata would benefit from debug logging to aid troubleshooting.

 private Mono<DatasourceStorage> setAdditionalMetadataInDatasourceStorage(DatasourceStorage datasourceStorage) {
+    log.debug("Setting metadata for datasource storage: {}", datasourceStorage.getId());
     Mono<String> tenantIdMono = tenantService.getDefaultTenantId();
     Mono<String> instanceIdMono = configService.getInstanceId();

     Map<String, Object> metadata = new HashMap<>();

     return tenantIdMono.zipWith(instanceIdMono).map(tuple -> {
         metadata.put(TENANT_ID, tuple.getT1());
         metadata.put(INSTANCE_ID, tuple.getT2());
         datasourceStorage.setMetadata(metadata);
+        log.debug("Metadata set successfully for datasource storage: {}", datasourceStorage.getId());
         return datasourceStorage;
     });
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1155a3 and 260ad93.

📒 Files selected for processing (3)
  • app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DatasourceStorage.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DatasourceStorage.java
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: perform-test / server-build / server-unit-tests
  • GitHub Check: perform-test / client-build / client-build
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: server-unit-tests / server-unit-tests
  • GitHub Check: server-spotless / spotless-check
🔇 Additional comments (3)
app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java (1)

200-200: LGTM! Addition aligns with tenant-specific datasource management.

The new constant follows the established naming conventions and is appropriately placed near related constants.

app/server/appsmith-server/src/main/java/com/appsmith/server/datasources/base/DatasourceServiceCEImpl.java (2)

100-101: LGTM! New service dependencies added.

The addition of TenantService and ConfigService as dependencies follows good dependency injection practices.


235-257: Verify error handling in the metadata setting chain.

The new flatMap chain for setting metadata should handle potential errors from tenant/config service calls.

Consider adding error handling:

 return datasourceMono.flatMap(savedDatasource -> this.organiseDatasourceStorages(savedDatasource)
         .flatMap(datasourceStorageX -> setAdditionalMetadataInDatasourceStorage(datasourceStorageX)
+                .onErrorResume(error -> {
+                    log.error("Error setting metadata in datasource storage", error);
+                    return Mono.just(datasourceStorageX);
+                })
                 .flatMap(datasourceStorage -> {

@nsarupr nsarupr requested a review from mohanarpit February 18, 2025 09:06
@nsarupr nsarupr merged commit d921110 into release Feb 18, 2025
45 checks passed
@nsarupr nsarupr deleted the chore/datasource-post-save-hook branch February 18, 2025 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ok-to-test Required label for CI skip-changelog Adding this label to a PR prevents it from being listed in the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants