chore: autocommit migration for annotation and embedded datasource changes. (#36261)

## Description
- Added autocommit migration to avoid uncommited changes on some of the
applications.


Fixes #`Issue Number`  
_or_  
Fixes `Issue URL`

## Automation

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

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!IMPORTANT]
> 🟣 🟣 🟣 Your tests are running.
> Tests running at:
<https://github.com/appsmithorg/appsmith/actions/runs/10878546679>
> Commit: 0013cdec8894922d3cae386a8d8d7b8aebc3837d
> Workflow: `PR Automation test suite`
> Tags: `@tag.Git`
> Spec: ``
> <hr>Mon, 16 Sep 2024 06:20:40 UTC
<!-- 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**
- Updated the server version from 10 to 11, enhancing compatibility and
functionality related to JSON schema handling.
- Improved logic for default REST datasource migrations, making it more
robust and accessible.
- Added support for SSH connection configurations in datasource
management.

- **Bug Fixes**
- Added null checks to prevent potential errors during datasource
migrations, enhancing overall reliability.

- **Refactor**
- Streamlined filtering logic for actions, improving code readability
and maintainability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Manish Kumar 2024-09-16 13:24:08 +05:30 committed by GitHub
parent 37282997dc
commit 71261b1e6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 95 additions and 40 deletions

View File

@ -34,8 +34,10 @@ public class DatasourceConfiguration implements AppsmithDomain {
@JsonView({Views.Public.class, FromRequest.class})
AuthenticationDTO authentication;
@JsonView({Views.Public.class, FromRequest.class})
SSHConnection sshProxy;
@JsonView({Views.Public.class, FromRequest.class})
Boolean sshProxyEnabled;
@JsonView({Views.Public.class, FromRequest.class, Git.class})

View File

@ -67,28 +67,38 @@ public class JsonSchemaMigration {
// TODO: make import flow migration reactive
return Mono.just(migrateServerSchema(appJson))
.flatMap(migratedApplicationJson -> {
if (migratedApplicationJson.getServerSchemaVersion() == 9
&& Boolean.TRUE.equals(MigrationHelperMethods.doesRestApiRequireMigration(
migratedApplicationJson))) {
return jsonSchemaMigrationHelper
.addDatasourceConfigurationToDefaultRestApiActions(
baseApplicationId, branchName, migratedApplicationJson)
.map(applicationJsonWithMigration10 -> {
applicationJsonWithMigration10.setServerSchemaVersion(10);
return applicationJsonWithMigration10;
});
// In Server version 9, there was a bug where the Embedded REST API datasource URL
// was not being persisted correctly. Once the bug was fixed,
// any previously uncommitted changes started appearing as uncommitted modifications
// in the apps. To automatically commit these changes
// (which were now appearing as uncommitted), a migration process was needed.
// This migration fetches the datasource URL from the database
// and serializes it in Git if the URL exists.
// If the URL is missing, it copies the empty datasource configuration
// if the configuration is present in the database.
// Otherwise, it leaves the configuration unchanged.
// Due to an update in the migration logic after version 10 was shipped,
// the entire migration process was moved to version 11.
// This adjustment ensures that the same operation can be
// performed again for the changes introduced in version 10.
if (migratedApplicationJson.getServerSchemaVersion() == 9) {
migratedApplicationJson.setServerSchemaVersion(10);
}
if (migratedApplicationJson.getServerSchemaVersion() == 10) {
if (Boolean.TRUE.equals(MigrationHelperMethods.doesRestApiRequireMigration(
migratedApplicationJson))) {
return jsonSchemaMigrationHelper
.addDatasourceConfigurationToDefaultRestApiActions(
baseApplicationId, branchName, migratedApplicationJson);
}
migratedApplicationJson.setServerSchemaVersion(11);
}
migratedApplicationJson.setServerSchemaVersion(10);
return Mono.just(migratedApplicationJson);
})
.map(migratedAppJson -> {
if (applicationJson
.getServerSchemaVersion()
.equals(jsonSchemaVersions.getServerVersion())) {
return applicationJson;
}
applicationJson.setServerSchemaVersion(jsonSchemaVersions.getServerVersion());
return applicationJson;
});
@ -193,16 +203,14 @@ public class JsonSchemaMigration {
switch (applicationJson.getServerSchemaVersion()) {
case 9:
applicationJson.setServerSchemaVersion(10);
case 10:
// this if for cases where we have empty datasource configs
MigrationHelperMethods.migrateApplicationJsonToVersionTen(applicationJson, Map.of());
applicationJson.setServerSchemaVersion(10);
applicationJson.setServerSchemaVersion(11);
default:
}
if (applicationJson.getServerSchemaVersion().equals(jsonSchemaVersions.getServerVersion())) {
return applicationJson;
}
applicationJson.setServerSchemaVersion(jsonSchemaVersions.getServerVersion());
return applicationJson;
}

View File

@ -4,7 +4,7 @@ import org.springframework.stereotype.Component;
@Component
public class JsonSchemaVersionsFallback {
private static final Integer serverVersion = 10;
private static final Integer serverVersion = 11;
public static final Integer clientVersion = 1;
public Integer getServerVersion() {

View File

@ -44,6 +44,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.appsmith.external.constants.PluginConstants.PackageName.GRAPHQL_PLUGIN;
import static com.appsmith.external.constants.PluginConstants.PackageName.REST_API_PLUGIN;
import static com.appsmith.server.constants.ResourceModes.EDIT;
import static com.appsmith.server.constants.ResourceModes.VIEW;
import static org.springframework.data.mongodb.core.query.Criteria.where;
@ -1235,13 +1237,34 @@ public class MigrationHelperMethods {
}
}
private static boolean conditionForDefaultRestDatasourceMigration(NewAction action) {
public static boolean conditionForDefaultRestDatasource(NewAction action) {
if (action.getUnpublishedAction() == null
|| action.getUnpublishedAction().getDatasource() == null) {
return false;
}
Datasource actionDatasource = action.getUnpublishedAction().getDatasource();
// probable check for the default rest datasource action is.
// it has no datasource id and action's plugin id is either rest-api or graphql plugin.
boolean probableCheckForDefaultRestDatasource = !org.springframework.util.StringUtils.hasText(
actionDatasource.getId())
&& (REST_API_PLUGIN.equals(action.getPluginId()) || GRAPHQL_PLUGIN.equals(action.getPluginId()));
// condition to check if the action is default rest datasource.
// it has no datasource id and name is equal to DEFAULT_REST_DATASOURCE
boolean isActionDefaultRestDatasource = !org.springframework.util.StringUtils.hasText(actionDatasource.getId())
&& PluginConstants.DEFAULT_REST_DATASOURCE.equals(actionDatasource.getName());
boolean certainCheckForDefaultRestDatasource =
!org.springframework.util.StringUtils.hasText(actionDatasource.getId())
&& PluginConstants.DEFAULT_REST_DATASOURCE.equals(actionDatasource.getName());
// Two separate types of checks over here, it's either the obvious certain way to identify or
// the likely chance that the datasource is present.
return certainCheckForDefaultRestDatasource || probableCheckForDefaultRestDatasource;
}
private static boolean conditionForDefaultRestDatasourceMigration(NewAction action) {
boolean isActionDefaultRestDatasource = conditionForDefaultRestDatasource(action);
Datasource actionDatasource = action.getUnpublishedAction().getDatasource();
// condition to check if the action has missing url or has no config at all
boolean isDatasourceConfigurationOrUrlMissing = actionDatasource.getDatasourceConfiguration() == null
@ -1322,18 +1345,25 @@ public class MigrationHelperMethods {
if (defaultDatasourceActionMap.containsKey(action.getGitSyncId())) {
NewAction actionFromMap = defaultDatasourceActionMap.get(action.getGitSyncId());
// NPE check to avoid migration failures
if (actionFromMap.getUnpublishedAction() == null
|| actionFromMap.getUnpublishedAction().getDatasource() == null
|| actionFromMap.getUnpublishedAction().getDatasource().getDatasourceConfiguration() == null) {
return;
}
// set the datasource config in the json action only if the datasource config from db is not null,
// else it'll start to show as uncommited changes.
DatasourceConfiguration datasourceConfigurationFromDBAction =
actionFromMap.getUnpublishedAction().getDatasource().getDatasourceConfiguration();
if (datasourceConfigurationFromDBAction != null) {
datasourceConfiguration.setUrl(datasourceConfigurationFromDBAction.getUrl());
}
}
// At this point it's established that datasource config of db action is not null.
datasourceConfiguration.setUrl(datasourceConfigurationFromDBAction.getUrl());
actionDatasource.setDatasourceConfiguration(datasourceConfiguration);
if (!org.springframework.util.StringUtils.hasText(datasourceConfiguration.getUrl())) {
} else {
datasourceConfiguration.setUrl("");
actionDatasource.setDatasourceConfiguration(datasourceConfiguration);
}
actionDatasource.setDatasourceConfiguration(datasourceConfiguration);
}
}

View File

@ -1,6 +1,8 @@
package com.appsmith.server.migrations.utils;
import com.appsmith.external.constants.PluginConstants;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.PluginType;
import com.appsmith.server.applications.base.ApplicationService;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.NewAction;
@ -51,14 +53,27 @@ public class JsonSchemaMigrationHelper {
return false;
}
boolean reverseFlag = StringUtils.hasText(action.getUnpublishedAction()
.getDatasource()
.getId())
|| !PluginConstants.DEFAULT_REST_DATASOURCE.equals(action.getUnpublishedAction()
.getDatasource()
.getName());
Datasource actionDatasource =
action.getUnpublishedAction().getDatasource();
return !reverseFlag;
// lenient probable check for the default rest datasource action is.
// As we don't have any harm in the allowing API actions present in db.
// it has no datasource id and action's plugin type is API
boolean probableCheckForDefaultRestDatasource =
!org.springframework.util.StringUtils.hasText(actionDatasource.getId())
&& PluginType.API.equals(action.getPluginType());
// condition to check if the action is default rest datasource.
// it has no datasource id and name is equal to DEFAULT_REST_DATASOURCE
boolean certainCheckForDefaultRestDatasource =
!org.springframework.util.StringUtils.hasText(actionDatasource.getId())
&& PluginConstants.DEFAULT_REST_DATASOURCE.equals(
actionDatasource.getName());
// Two separate types of checks over here, it's either the obvious certain way to
// identify or
// the likely chance that the datasource is present.
return certainCheckForDefaultRestDatasource || probableCheckForDefaultRestDatasource;
})
.collectMap(NewAction::getGitSyncId);
})