fix: Removed structure data from within datasource collection (#22847)

## Description

This PR is a structural change to our database and introduces no change
in functionality.

Since generate CRUD functionality is controlled by an Appsmith app in
release env, this PR will end up breaking generate CRUD temporarily.

Fixes https://github.com/appsmithorg/appsmith-ee/issues/1336

## Type of change
- Chore (housekeeping or task changes that don't impact user perception)

## How Has This Been Tested?
- Manual
- Jest

### Test Plan
Available
[here](https://docs.google.com/spreadsheets/d/1dP-x8b7gBFOfXtVBwr-A0I4M0nTiJmeysZO2ExI1Hhk/edit#gid=1966367141)

### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)


## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


### QA activity:
- [x] Test plan has been approved by relevant developers
- [x] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
This commit is contained in:
Nidhi 2023-05-03 10:18:29 +05:30 committed by GitHub
parent 9d31458683
commit c44a4a124b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1167 additions and 892 deletions

View File

@ -74,12 +74,6 @@ public class Datasource extends BranchAwareDomain {
@JsonView(Views.Internal.class)
Boolean isAutoGenerated = false;
// The structure is ignored in JSON as it is not sent as part of the datasources API. We have a separate endpoint
// to obtain the structure of the datasource. The value of this field serves as the cache.
@JsonView(Views.Internal.class)
DatasourceStructure structure;
/*
* This field is introduced as part of git sync feature, for the git import we will need to identify the datasource's
* which are not configured. This way user can configure those datasource, which may have been introduced as part of git import.
@ -139,7 +133,6 @@ public class Datasource extends BranchAwareDomain {
public void sanitiseToExportResource(Map<String, String> pluginMap) {
this.setPolicies(null);
this.setStructure(null);
this.setUpdatedAt(null);
this.setCreatedAt(null);
this.setUserPermissions(null);

View File

@ -0,0 +1,26 @@
package com.appsmith.external.models;
import com.appsmith.external.views.Views;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.mongodb.core.mapping.Document;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Document
public class DatasourceConfigurationStructure extends BaseDomain {
@JsonView(Views.Public.class)
private String datasourceId;
@JsonView(Views.Public.class)
private String environmentId;
@JsonView(Views.Internal.class)
private DatasourceStructure structure;
}

View File

@ -1,17 +1,17 @@
package com.appsmith.server.dtos;
import com.appsmith.external.models.Datasource;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.external.models.DecryptedSensitiveFields;
import com.appsmith.external.models.InvisibleActionFields;
import com.appsmith.external.views.Views;
import com.appsmith.server.domains.ActionCollection;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.server.domains.NewAction;
import com.appsmith.server.domains.NewPage;
import com.appsmith.server.domains.Theme;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Transient;
@ -46,6 +46,9 @@ public class ApplicationJson {
@JsonView(Views.Public.class)
List<Datasource> datasourceList;
@JsonView(Views.Public.class)
List<DatasourceConfigurationStructure> datasourceConfigurationStructureList;
@JsonView({Views.Public.class, Views.Export.class})
List<CustomJSLib> customJSLibList;
@ -67,7 +70,7 @@ public class ApplicationJson {
@Deprecated
@JsonView(Views.Public.class)
String unpublishedDefaultPageName;
@JsonView(Views.Public.class)
List<NewAction> actionList;

View File

@ -2279,10 +2279,10 @@ public class DatabaseChangelog1 {
Query query = query(new Criteria().andOperator(
where(fieldName(QDatasource.datasource.pluginId)).is(mongoPlugin.getId()),
where(fieldName(QDatasource.datasource.structure)).exists(true)
where("structure").exists(true)
));
Update update = new Update().set(fieldName(QDatasource.datasource.structure), null);
Update update = new Update().set("structure", null);
// Delete all the existing mongo datasource structures by setting the key to null.
mongoOperations.updateMulti(query, update, Datasource.class);
@ -4989,7 +4989,7 @@ public class DatabaseChangelog1 {
/* set key formData.smartSubstitution */
setSmartSubstitutionFieldForEachAction(firestoreActions, mongoTemplate);
}
private void setSmartSubstitutionFieldForEachAction(List<NewAction> firestoreActions,
MongoTemplate mongoTemplate) {
firestoreActions.stream()

View File

@ -0,0 +1,51 @@
package com.appsmith.server.migrations.db.ce;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.server.migrations.DatabaseChangelog1;
import io.mongock.api.annotations.ChangeUnit;
import io.mongock.api.annotations.Execution;
import io.mongock.api.annotations.RollbackExecution;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
@ChangeUnit(order = "009", id = "remove-structure-from-within-datasource")
public class Migration009RemoveStructureFromWithinDatasource {
private final MongoOperations mongoOperations;
private final MongoTemplate mongoTemplate;
public Migration009RemoveStructureFromWithinDatasource(MongoOperations mongoOperations,
MongoTemplate mongoTemplate) {
this.mongoOperations = mongoOperations;
this.mongoTemplate = mongoTemplate;
}
@RollbackExecution
public void rollBackExecution() {
// We don't need a rollback strategy because we don't care about this value anymore
}
@Execution
public void executeMigration() {
DatabaseChangelog1.dropIndexIfExists(mongoTemplate, DatasourceConfigurationStructure.class, "dsConfigStructure_datasourceId_envId_compound_index");
DatabaseChangelog1.ensureIndexes(mongoTemplate, DatasourceConfigurationStructure.class,
DatabaseChangelog1.makeIndex("datasourceId", "envId")
.unique().named("dsConfigStructure_datasourceId_envId_compound_index")
);
Query query = query(where("structure").exists(true));
Update update = new Update().unset("structure");
mongoOperations.updateMulti(query, update, Datasource.class);
}
}

View File

@ -0,0 +1,6 @@
package com.appsmith.server.repositories;
import com.appsmith.server.repositories.ce.CustomDatasourceConfigurationStructureRepositoryCE;
public interface CustomDatasourceConfigurationStructureRepository extends CustomDatasourceConfigurationStructureRepositoryCE {
}

View File

@ -0,0 +1,15 @@
package com.appsmith.server.repositories;
import com.appsmith.server.repositories.ce.CustomDatasourceConfigurationStructureRepositoryCEImpl;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
public class CustomDatasourceConfigurationStructureRepositoryImpl
extends CustomDatasourceConfigurationStructureRepositoryCEImpl
implements CustomDatasourceConfigurationStructureRepository {
public CustomDatasourceConfigurationStructureRepositoryImpl(ReactiveMongoOperations mongoOperations,
MongoConverter mongoConverter,
CacheableRepositoryHelper cacheableRepositoryHelper) {
super(mongoOperations, mongoConverter, cacheableRepositoryHelper);
}
}

View File

@ -0,0 +1,8 @@
package com.appsmith.server.repositories;
import com.appsmith.server.repositories.ce.DatasourceConfigurationStructureRepositoryCE;
import org.springframework.stereotype.Repository;
@Repository
public interface DatasourceConfigurationStructureRepository extends DatasourceConfigurationStructureRepositoryCE, CustomDatasourceConfigurationStructureRepository {
}

View File

@ -0,0 +1,10 @@
package com.appsmith.server.repositories.ce;
import com.appsmith.external.models.DatasourceStructure;
import com.mongodb.client.result.UpdateResult;
import reactor.core.publisher.Mono;
public interface CustomDatasourceConfigurationStructureRepositoryCE {
Mono<UpdateResult> updateStructure(String datasourceId, DatasourceStructure structure);
}

View File

@ -0,0 +1,36 @@
package com.appsmith.server.repositories.ce;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.QDatasourceConfigurationStructure;
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
import com.appsmith.server.repositories.CacheableRepositoryHelper;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
@Component
public class CustomDatasourceConfigurationStructureRepositoryCEImpl
extends BaseAppsmithRepositoryImpl<DatasourceConfigurationStructure>
implements CustomDatasourceConfigurationStructureRepositoryCE {
public CustomDatasourceConfigurationStructureRepositoryCEImpl(ReactiveMongoOperations mongoOperations,
MongoConverter mongoConverter,
CacheableRepositoryHelper cacheableRepositoryHelper) {
super(mongoOperations, mongoConverter, cacheableRepositoryHelper);
}
@Override
public Mono<UpdateResult> updateStructure(String datasourceId, DatasourceStructure structure) {
return mongoOperations.upsert(
query(where(fieldName(QDatasourceConfigurationStructure.datasourceConfigurationStructure.datasourceId)).is(datasourceId)),
Update.update(fieldName(QDatasourceConfigurationStructure.datasourceConfigurationStructure.structure), structure),
DatasourceConfigurationStructure.class
);
}
}

View File

@ -1,10 +1,8 @@
package com.appsmith.server.repositories.ce;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.repositories.AppsmithRepository;
import com.mongodb.client.result.UpdateResult;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -25,6 +23,4 @@ public interface CustomDatasourceRepositoryCE extends AppsmithRepository<Datasou
Flux<Datasource> findAllByIds(Set<String> ids, AclPermission permission);
Mono<UpdateResult> saveStructure(String datasourceId, DatasourceStructure structure);
}

View File

@ -1,17 +1,14 @@
package com.appsmith.server.repositories.ce;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.QDatasource;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
import com.appsmith.server.repositories.CacheableRepositoryHelper;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Update;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -20,7 +17,6 @@ import java.util.Optional;
import java.util.Set;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
public class CustomDatasourceRepositoryCEImpl extends BaseAppsmithRepositoryImpl<Datasource> implements CustomDatasourceRepositoryCE {
@ -61,12 +57,4 @@ public class CustomDatasourceRepositoryCEImpl extends BaseAppsmithRepositoryImpl
Criteria idcriteria = where(fieldName(QDatasource.datasource.id)).in(ids);
return queryAll(List.of(idcriteria), permission);
}
public Mono<UpdateResult> saveStructure(String datasourceId, DatasourceStructure structure) {
return mongoOperations.updateFirst(
query(where(fieldName(QDatasource.datasource.id)).is(datasourceId)),
Update.update(fieldName(QDatasource.datasource.structure), structure),
Datasource.class
);
}
}

View File

@ -0,0 +1,13 @@
package com.appsmith.server.repositories.ce;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.server.repositories.BaseRepository;
import com.appsmith.server.repositories.CustomDatasourceConfigurationStructureRepository;
import reactor.core.publisher.Mono;
public interface DatasourceConfigurationStructureRepositoryCE
extends BaseRepository<DatasourceConfigurationStructure, String>, CustomDatasourceConfigurationStructureRepository {
Mono<DatasourceConfigurationStructure> findByDatasourceId(String datasourceId);
}

View File

@ -0,0 +1,7 @@
package com.appsmith.server.services;
import com.appsmith.server.services.ce.DatasourceConfigurationStructureServiceCE;
public interface DatasourceConfigurationStructureService extends DatasourceConfigurationStructureServiceCE {
}

View File

@ -0,0 +1,14 @@
package com.appsmith.server.services;
import com.appsmith.server.repositories.DatasourceConfigurationStructureRepository;
import com.appsmith.server.services.ce.DatasourceConfigurationStructureServiceCEImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class DatasourceConfigurationStructureServiceImpl extends DatasourceConfigurationStructureServiceCEImpl implements DatasourceConfigurationStructureService {
public DatasourceConfigurationStructureServiceImpl(DatasourceConfigurationStructureRepository repository) {
super(repository);
}
}

View File

@ -224,13 +224,14 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ
@Override
public Mono<List<ApplicationTemplate>> getRecentlyUsedTemplates() {
return userDataService.getForCurrentUser().flatMap(userData -> {
List<String> templateIds = userData.getRecentlyUsedTemplateIds();
if (!CollectionUtils.isEmpty(templateIds)) {
return getActiveTemplates(templateIds);
}
return Mono.empty();
});
return userDataService.getForCurrentUser()
.flatMap(userData -> {
List<String> templateIds = userData.getRecentlyUsedTemplateIds();
if (!CollectionUtils.isEmpty(templateIds)) {
return getActiveTemplates(templateIds);
}
return Mono.empty();
});
}
@Override
@ -274,7 +275,7 @@ public class ApplicationTemplateServiceCEImpl implements ApplicationTemplateServ
String branchName,
List<String> pagesToImport) {
Mono<ApplicationImportDTO> importedApplicationMono = getApplicationJsonFromTemplate(templateId)
.flatMap(applicationJson ->{
.flatMap(applicationJson -> {
if (branchName != null) {
return applicationService.findByBranchNameAndDefaultApplicationId(branchName, applicationId, applicationPermission.getEditPermission())
.flatMap(application -> importExportApplicationService.mergeApplicationJsonWithApplication(organizationId, application.getId(), branchName, applicationJson, pagesToImport));

View File

@ -0,0 +1,15 @@
package com.appsmith.server.services.ce;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.external.models.DatasourceStructure;
import com.mongodb.client.result.UpdateResult;
import reactor.core.publisher.Mono;
public interface DatasourceConfigurationStructureServiceCE {
Mono<DatasourceConfigurationStructure> getByDatasourceId(String datasourceId);
Mono<DatasourceConfigurationStructure> save(DatasourceConfigurationStructure datasourceConfigurationStructure);
Mono<UpdateResult> saveStructure(String datasourceId, DatasourceStructure structure);
}

View File

@ -0,0 +1,31 @@
package com.appsmith.server.services.ce;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.server.repositories.DatasourceConfigurationStructureRepository;
import com.mongodb.client.result.UpdateResult;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@AllArgsConstructor
@Service
public class DatasourceConfigurationStructureServiceCEImpl implements DatasourceConfigurationStructureServiceCE {
protected final DatasourceConfigurationStructureRepository repository;
@Override
public Mono<DatasourceConfigurationStructure> getByDatasourceId(String datasourceId) {
return repository.findByDatasourceId(datasourceId);
}
@Override
public Mono<DatasourceConfigurationStructure> save(DatasourceConfigurationStructure datasourceConfigurationStructure) {
return repository.save(datasourceConfigurationStructure);
}
@Override
public Mono<UpdateResult> saveStructure(String datasourceId, DatasourceStructure structure) {
return repository.updateStructure(datasourceId, structure);
}
}

View File

@ -1,8 +1,8 @@
package com.appsmith.server.solutions;
import com.appsmith.server.helpers.PluginExecutorHelper;
import com.appsmith.server.repositories.CustomDatasourceRepository;
import com.appsmith.server.services.AuthenticationValidator;
import com.appsmith.server.services.DatasourceConfigurationStructureService;
import com.appsmith.server.services.DatasourceContextService;
import com.appsmith.server.services.DatasourceService;
import com.appsmith.server.services.PluginService;
@ -18,11 +18,11 @@ public class DatasourceStructureSolutionImpl extends DatasourceStructureSolution
PluginExecutorHelper pluginExecutorHelper,
PluginService pluginService,
DatasourceContextService datasourceContextService,
CustomDatasourceRepository datasourceRepository,
AuthenticationValidator authenticationValidator,
DatasourcePermission datasourcePermission) {
DatasourcePermission datasourcePermission,
DatasourceConfigurationStructureService datasourceConfigurationStructureService) {
super(datasourceService, pluginExecutorHelper, pluginService, datasourceContextService, datasourceRepository,
authenticationValidator, datasourcePermission);
super(datasourceService, pluginExecutorHelper, pluginService, datasourceContextService,
authenticationValidator, datasourcePermission, datasourceConfigurationStructureService);
}
}

View File

@ -7,6 +7,7 @@ import com.appsmith.external.helpers.AppsmithBeanUtils;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.DatasourceStructure.Column;
import com.appsmith.external.models.DatasourceStructure.PrimaryKey;
@ -273,7 +274,15 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
);
}
DatasourceStructure templateStructure = templateDatasource.getStructure();
DatasourceConfigurationStructure templateDatasourceConfigurationStructure = applicationJson
.getDatasourceConfigurationStructureList()
.stream()
.filter(configurationStructure -> StringUtils.equals(configurationStructure.getDatasourceId(), templateDatasource.getName()))
.findAny()
.orElse(null);
DatasourceStructure templateStructure = templateDatasourceConfigurationStructure.getStructure();
// We are supporting datasources for both with and without datasource structure. So if datasource
// structure is present then we can assign the mapping dynamically as per the template datasource tables.
// Those datasources for which we don't have structure like Google sheet etc we are following a
@ -287,7 +296,7 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
.findAny()
.orElse(null);
Table table = getTable(datasource, tableName);
Table table = getTable(templateDatasourceConfigurationStructure, tableName);
if (table == null) {
return Mono.error(
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.DATASOURCE_STRUCTURE, datasource.getName())
@ -464,16 +473,16 @@ public class CreateDBTablePageSolutionCEImpl implements CreateDBTablePageSolutio
}
/**
* @param datasource resource from which table has to be filtered
* @param tableName to filter the available tables in the datasource
* @param datasourceConfigurationStructure resource from which table has to be filtered
* @param tableName to filter the available tables in the datasource
* @return Table from the provided datasource if structure is present
*/
private Table getTable(Datasource datasource, String tableName) {
private Table getTable(DatasourceConfigurationStructure datasourceConfigurationStructure, String tableName) {
/*
1. Get structure from datasource
2. Filter by tableName
*/
DatasourceStructure datasourceStructure = datasource.getStructure();
DatasourceStructure datasourceStructure = datasourceConfigurationStructure.getStructure();
if (datasourceStructure != null) {
return datasourceStructure.getTables()
.stream()

View File

@ -12,7 +12,10 @@ public interface DatasourceStructureSolutionCE {
Mono<DatasourceStructure> getStructure(String datasourceId, boolean ignoreCache, String environmentName);
Mono<DatasourceStructure> getStructure(Datasource datasource, boolean ignoreCache, String environmentName);
Mono<DatasourceStructure> getStructure(
Datasource datasource,
boolean ignoreCache,
String environmentName);
Mono<ActionExecutionResult> getDatasourceMetadata(String datasourceId, List<Property> pluginSpecifiedTemplates);

View File

@ -7,6 +7,7 @@ import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.AuthenticationDTO;
import com.appsmith.external.models.BaseDomain;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceConfigurationStructure;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.Property;
import com.appsmith.external.plugins.PluginExecutor;
@ -15,8 +16,8 @@ import com.appsmith.server.domains.DatasourceContextIdentifier;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.PluginExecutorHelper;
import com.appsmith.server.repositories.CustomDatasourceRepository;
import com.appsmith.server.services.AuthenticationValidator;
import com.appsmith.server.services.DatasourceConfigurationStructureService;
import com.appsmith.server.services.DatasourceContextService;
import com.appsmith.server.services.DatasourceService;
import com.appsmith.server.services.PluginService;
@ -41,10 +42,9 @@ public class DatasourceStructureSolutionCEImpl implements DatasourceStructureSol
private final PluginExecutorHelper pluginExecutorHelper;
private final PluginService pluginService;
private final DatasourceContextService datasourceContextService;
private final CustomDatasourceRepository datasourceRepository;
private final AuthenticationValidator authenticationValidator;
private final DatasourcePermission datasourcePermission;
private final DatasourceConfigurationStructureService datasourceConfigurationStructureService;
public Mono<DatasourceStructure> getStructure(String datasourceId, boolean ignoreCache, String environmentName) {
return datasourceService.getById(datasourceId)
@ -72,19 +72,17 @@ public class DatasourceStructureSolutionCEImpl implements DatasourceStructureSol
});
}
public Mono<DatasourceStructure> getStructure(Datasource datasource, boolean ignoreCache, String environmentName) {
public Mono<DatasourceStructure> getStructure(Datasource datasource,
boolean ignoreCache,
String environmentName) {
if (Boolean.TRUE.equals(datasourceHasInvalids(datasource))) {
return Mono.empty();
}
if (!ignoreCache && datasource.getStructure() != null) {
// Return the cached structure if available.
return Mono.just(datasource.getStructure());
}
Mono<DatasourceConfigurationStructure> configurationStructureMono = datasourceConfigurationStructureService.getByDatasourceId(datasource.getId());
// This mono, when computed, will load the structure of the datasource by calling the plugin method.
return pluginExecutorHelper
Mono<DatasourceStructure> fetchAndStoreNewStructureMono = pluginExecutorHelper
.getPluginExecutor(pluginService.findById(datasource.getPluginId()))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.PLUGIN, datasource.getPluginId())))
.flatMap(pluginExecutor -> datasourceService
@ -95,9 +93,9 @@ public class DatasourceStructureSolutionCEImpl implements DatasourceStructureSol
Map<String, BaseDomain> environmentMap = tuple3.getT3();
return datasourceContextService
.retryOnce(datasource2, datasourceContextIdentifier, environmentMap,
resourceContext -> ((PluginExecutor<Object>) pluginExecutor)
.getStructure(resourceContext.getConnection(),
datasource2.getDatasourceConfiguration())); // this datasourceConfiguration is unevaluated for DBAuth type.
resourceContext -> ((PluginExecutor<Object>) pluginExecutor)
.getStructure(resourceContext.getConnection(),
datasource2.getDatasourceConfiguration())); // this datasourceConfiguration is unevaluated for DBAuth type.
}))
.timeout(Duration.ofSeconds(GET_STRUCTURE_TIMEOUT_SECONDS))
.onErrorMap(
@ -135,8 +133,20 @@ public class DatasourceStructureSolutionCEImpl implements DatasourceStructureSol
})
.flatMap(structure -> datasource.getId() == null
? Mono.empty()
: datasourceRepository.saveStructure(datasource.getId(), structure).thenReturn(structure)
: datasourceConfigurationStructureService.saveStructure(datasource.getId(), structure).thenReturn(structure)
);
// This mono, when computed, will load the structure of the datasource by calling the plugin method.
return configurationStructureMono
.flatMap(configurationStructure -> {
if (!ignoreCache && configurationStructure.getStructure() != null) {
// Return the cached structure if available.
return Mono.just(configurationStructure.getStructure());
} else return Mono.empty();
})
.switchIfEmpty(fetchAndStoreNewStructureMono);
}
/**
@ -187,6 +197,7 @@ public class DatasourceStructureSolutionCEImpl implements DatasourceStructureSol
/**
* Checks if the datasource has any invalids.
* This will have EE overrides.
*
* @param datasource
* @return true if datasource has invalids, otherwise a false
*/

View File

@ -51,6 +51,7 @@ public class DatasourceTriggerSolutionCEImpl implements DatasourceTriggerSolutio
Mono<Datasource> datasourceMono = datasourceService.findById(datasourceId, datasourcePermission.getReadPermission())
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "datasourceId")))
.cache();
final Mono<Plugin> pluginMono = datasourceMono
.map(datasource -> datasource.getPluginId())
.flatMap(pluginId -> pluginService.findById(pluginId))
@ -73,7 +74,7 @@ public class DatasourceTriggerSolutionCEImpl implements DatasourceTriggerSolutio
Mono<Tuple5> datasourceAndPluginEssentialsMono =
datasourceMono.flatMap(datasource -> {
return datasourceService.getEvaluatedDSAndDsContextKeyWithEnvMap(datasource, environmentName)
.flatMap(tuple3-> {
.flatMap(tuple3 -> {
Datasource datasource1 = tuple3.getT1();
DatasourceContextIdentifier datasourceContextIdentifier = tuple3.getT2();
Map<String, BaseDomain> environmentMap = tuple3.getT3();
@ -84,7 +85,7 @@ public class DatasourceTriggerSolutionCEImpl implements DatasourceTriggerSolutio
.cache();
return Mono.zip(validatedDatasourceMono, pluginMono, pluginExecutorMono,
Mono.just(datasourceContextIdentifier), Mono.just(environmentMap));
Mono.just(datasourceContextIdentifier), Mono.just(environmentMap));
});
});
@ -106,7 +107,7 @@ public class DatasourceTriggerSolutionCEImpl implements DatasourceTriggerSolutio
return datasourceContextService.getRemoteDatasourceContext(plugin, datasource1);
} else {
return datasourceContextService.getDatasourceContext(datasource, datasourceContextIdentifier,
environmentMap);
environmentMap);
}
})
// Now that we have the context (connection details), execute the action.
@ -114,14 +115,14 @@ public class DatasourceTriggerSolutionCEImpl implements DatasourceTriggerSolutio
// However the context comes from evaluated datasource.
.flatMap(resourceContext -> {
return (Mono<TriggerResultDTO>) pluginExecutor.trigger(resourceContext.getConnection(),
datasource.getDatasourceConfiguration(),
triggerRequestDTO);
datasource.getDatasourceConfiguration(),
triggerRequestDTO);
});
});
// If the plugin hasn't, go for the default implementation
Mono<TriggerResultDTO> defaultResultMono = datasourceMono
.flatMap(datasource -> entitySelectorTriggerSolution(datasource, triggerRequestDTO, environmentName))
.flatMap(datasource -> entitySelectorTriggerSolution(datasourceId, triggerRequestDTO, environmentName))
.map(entityNames -> {
List<Object> result = new ArrayList<>();
@ -142,15 +143,15 @@ public class DatasourceTriggerSolutionCEImpl implements DatasourceTriggerSolutio
.switchIfEmpty(defaultResultMono);
}
private Mono<Set<String>> entitySelectorTriggerSolution(Datasource datasource, TriggerRequestDTO request,
private Mono<Set<String>> entitySelectorTriggerSolution(String datasourceId, TriggerRequestDTO request,
String environmentName) {
if (request.getDisplayType() == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, DISPLAY_TYPE));
}
final Map<String, Object> parameters = request.getParameters();
Mono<DatasourceStructure> structureMono = datasourceStructureSolution.getStructure(datasource, false,
environmentName);
Mono<DatasourceStructure> structureMono = datasourceStructureSolution.getStructure(datasourceId, false,
environmentName);
return structureMono
.map(structure -> {

View File

@ -821,7 +821,6 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
datasource.setDatasourceConfiguration(null);
datasource.setPluginId(null);
copyNestedNonNullProperties(datasource, existingDatasource);
existingDatasource.setStructure(null);
// Don't update the datasource configuration for already available datasources
existingDatasource.setDatasourceConfiguration(null);
return datasourceService.update(existingDatasource.getId(), existingDatasource);
@ -847,81 +846,81 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
.collectMap(Datasource::getName, Datasource::getId)
.flatMap(map -> {
datasourceMap.putAll(map);
// 1. Assign the policies for the imported application
// 2. Check for possible duplicate names,
// 3. Save the updated application
datasourceMap.putAll(map);
// 1. Assign the policies for the imported application
// 2. Check for possible duplicate names,
// 3. Save the updated application
return Mono.just(importedApplication)
.zipWith(currUserMono)
.map(objects -> {
Application application = objects.getT1();
application.setModifiedBy(objects.getT2().getUsername());
return application;
})
.flatMap(application -> {
importedApplication.setWorkspaceId(workspaceId);
// Application Id will be present for GIT sync
if (!StringUtils.isEmpty(applicationId)) {
return applicationService.findById(applicationId, applicationPermission.getEditPermission())
.switchIfEmpty(
Mono.error(new AppsmithException(
AppsmithError.ACL_NO_RESOURCE_FOUND,
FieldName.APPLICATION_ID,
applicationId))
)
.flatMap(existingApplication -> {
if (appendToApp) {
// When we are appending the pages to the existing application
// e.g. import template we are only importing this in unpublished
// version. At the same time we want to keep the existing page ref
unpublishedPages.addAll(existingApplication.getPages());
return Mono.just(existingApplication);
}
importedApplication.setId(existingApplication.getId());
// For the existing application we don't need to default value of the flag
// The isPublic flag has a default value as false and this would be confusing to user
// when it is reset to false during importing where the application already is present in DB
importedApplication.setIsPublic(null);
importedApplication.setPolicies(null);
copyNestedNonNullProperties(importedApplication, existingApplication);
// We are expecting the changes present in DB are committed to git directory
// so that these won't be lost when we are pulling changes from remote and
// rehydrate the application. We are now rehydrating the application with/without
// the changes from remote
// We are using the save instead of update as we are using @Encrypted
// for GitAuth
Mono<Application> parentApplicationMono;
if (existingApplication.getGitApplicationMetadata() != null) {
parentApplicationMono = applicationService.findById(
existingApplication.getGitApplicationMetadata().getDefaultApplicationId()
);
} else {
parentApplicationMono = Mono.just(existingApplication);
}
return Mono.just(importedApplication)
.zipWith(currUserMono)
.map(objects -> {
Application application = objects.getT1();
application.setModifiedBy(objects.getT2().getUsername());
return application;
})
.flatMap(application -> {
importedApplication.setWorkspaceId(workspaceId);
// Application Id will be present for GIT sync
if (!StringUtils.isEmpty(applicationId)) {
return applicationService.findById(applicationId, applicationPermission.getEditPermission())
.switchIfEmpty(
Mono.error(new AppsmithException(
AppsmithError.ACL_NO_RESOURCE_FOUND,
FieldName.APPLICATION_ID,
applicationId))
)
.flatMap(existingApplication -> {
if (appendToApp) {
// When we are appending the pages to the existing application
// e.g. import template we are only importing this in unpublished
// version. At the same time we want to keep the existing page ref
unpublishedPages.addAll(existingApplication.getPages());
return Mono.just(existingApplication);
}
importedApplication.setId(existingApplication.getId());
// For the existing application we don't need to default value of the flag
// The isPublic flag has a default value as false and this would be confusing to user
// when it is reset to false during importing where the application already is present in DB
importedApplication.setIsPublic(null);
importedApplication.setPolicies(null);
copyNestedNonNullProperties(importedApplication, existingApplication);
// We are expecting the changes present in DB are committed to git directory
// so that these won't be lost when we are pulling changes from remote and
// rehydrate the application. We are now rehydrating the application with/without
// the changes from remote
// We are using the save instead of update as we are using @Encrypted
// for GitAuth
Mono<Application> parentApplicationMono;
if (existingApplication.getGitApplicationMetadata() != null) {
parentApplicationMono = applicationService.findById(
existingApplication.getGitApplicationMetadata().getDefaultApplicationId()
);
} else {
parentApplicationMono = Mono.just(existingApplication);
}
return parentApplicationMono
.flatMap(application1 -> {
// Set the policies from the defaultApplication
existingApplication.setPolicies(application1.getPolicies());
importedApplication.setPolicies(application1.getPolicies());
return applicationService.save(existingApplication)
.onErrorResume(DuplicateKeyException.class, error -> {
if (error.getMessage() != null) {
return applicationPageService
.createOrUpdateSuffixedApplication(
existingApplication,
existingApplication.getName(),
0
);
}
throw error;
});
});
});
}
return applicationPageService.createOrUpdateSuffixedApplication(application, application.getName(), 0);
});
return parentApplicationMono
.flatMap(application1 -> {
// Set the policies from the defaultApplication
existingApplication.setPolicies(application1.getPolicies());
importedApplication.setPolicies(application1.getPolicies());
return applicationService.save(existingApplication)
.onErrorResume(DuplicateKeyException.class, error -> {
if (error.getMessage() != null) {
return applicationPageService
.createOrUpdateSuffixedApplication(
existingApplication,
existingApplication.getName(),
0
);
}
throw error;
});
});
});
}
return applicationPageService.createOrUpdateSuffixedApplication(application, application.getName(), 0);
});
}
)
.flatMap(savedApp -> importThemes(savedApp, importedDoc, appendToApp))

View File

@ -224,8 +224,7 @@ public class ImportExportApplicationServiceCEImplV2 implements ImportExportAppli
applicationLastCommittedAt.isBefore(lib.getUpdatedAt()))
.map(lib -> lib.getUidString())
.collect(Collectors.toList());
}
else {
} else {
updatedCustomJSLibList = unpublishedCustomJSLibList
.stream()
.map(lib -> lib.getUidString())
@ -843,7 +842,6 @@ public class ImportExportApplicationServiceCEImplV2 implements ImportExportAppli
datasource.setDatasourceConfiguration(null);
datasource.setPluginId(null);
copyNestedNonNullProperties(datasource, existingDatasource);
existingDatasource.setStructure(null);
// Don't update the datasource configuration for already available datasources
existingDatasource.setDatasourceConfiguration(null);
return datasourceService.update(existingDatasource.getId(), existingDatasource);
@ -868,82 +866,82 @@ public class ImportExportApplicationServiceCEImplV2 implements ImportExportAppli
})
.collectMap(Datasource::getName, Datasource::getId)
.flatMap(map -> {
datasourceMap.putAll(map);
// 1. Assign the policies for the imported application
// 2. Check for possible duplicate names,
// 3. Save the updated application
datasourceMap.putAll(map);
// 1. Assign the policies for the imported application
// 2. Check for possible duplicate names,
// 3. Save the updated application
return Mono.just(importedApplication)
.zipWith(currUserMono)
.map(objects -> {
Application application = objects.getT1();
application.setModifiedBy(objects.getT2().getUsername());
return application;
})
.flatMap(application -> {
importedApplication.setWorkspaceId(workspaceId);
// Application Id will be present for GIT sync
if (!StringUtils.isEmpty(applicationId)) {
return applicationService.findById(applicationId, Optional.empty())
.switchIfEmpty(
Mono.error(new AppsmithException(
AppsmithError.ACL_NO_RESOURCE_FOUND,
FieldName.APPLICATION_ID,
applicationId))
)
.flatMap(existingApplication -> {
if (appendToApp) {
// When we are appending the pages to the existing application
// e.g. import template we are only importing this in unpublished
// version. At the same time we want to keep the existing page ref
unpublishedPages.addAll(existingApplication.getPages());
return Mono.just(existingApplication);
}
importedApplication.setId(existingApplication.getId());
// For the existing application we don't need to default value of the flag
// The isPublic flag has a default value as false and this would be confusing to user
// when it is reset to false during importing where the application already is present in DB
importedApplication.setIsPublic(null);
importedApplication.setPolicies(null);
copyNestedNonNullProperties(importedApplication, existingApplication);
// We are expecting the changes present in DB are committed to git directory
// so that these won't be lost when we are pulling changes from remote and
// rehydrate the application. We are now rehydrating the application with/without
// the changes from remote
// We are using the save instead of update as we are using @Encrypted
// for GitAuth
Mono<Application> parentApplicationMono;
if (existingApplication.getGitApplicationMetadata() != null) {
parentApplicationMono = applicationService.findById(
existingApplication.getGitApplicationMetadata().getDefaultApplicationId()
);
} else {
parentApplicationMono = Mono.just(existingApplication);
}
return Mono.just(importedApplication)
.zipWith(currUserMono)
.map(objects -> {
Application application = objects.getT1();
application.setModifiedBy(objects.getT2().getUsername());
return application;
})
.flatMap(application -> {
importedApplication.setWorkspaceId(workspaceId);
// Application Id will be present for GIT sync
if (!StringUtils.isEmpty(applicationId)) {
return applicationService.findById(applicationId, Optional.empty())
.switchIfEmpty(
Mono.error(new AppsmithException(
AppsmithError.ACL_NO_RESOURCE_FOUND,
FieldName.APPLICATION_ID,
applicationId))
)
.flatMap(existingApplication -> {
if (appendToApp) {
// When we are appending the pages to the existing application
// e.g. import template we are only importing this in unpublished
// version. At the same time we want to keep the existing page ref
unpublishedPages.addAll(existingApplication.getPages());
return Mono.just(existingApplication);
}
importedApplication.setId(existingApplication.getId());
// For the existing application we don't need to default value of the flag
// The isPublic flag has a default value as false and this would be confusing to user
// when it is reset to false during importing where the application already is present in DB
importedApplication.setIsPublic(null);
importedApplication.setPolicies(null);
copyNestedNonNullProperties(importedApplication, existingApplication);
// We are expecting the changes present in DB are committed to git directory
// so that these won't be lost when we are pulling changes from remote and
// rehydrate the application. We are now rehydrating the application with/without
// the changes from remote
// We are using the save instead of update as we are using @Encrypted
// for GitAuth
Mono<Application> parentApplicationMono;
if (existingApplication.getGitApplicationMetadata() != null) {
parentApplicationMono = applicationService.findById(
existingApplication.getGitApplicationMetadata().getDefaultApplicationId()
);
} else {
parentApplicationMono = Mono.just(existingApplication);
}
return parentApplicationMono
.flatMap(application1 -> {
// Set the policies from the defaultApplication
existingApplication.setPolicies(application1.getPolicies());
importedApplication.setPolicies(application1.getPolicies());
return applicationService.save(existingApplication)
.onErrorResume(DuplicateKeyException.class, error -> {
if (error.getMessage() != null) {
return applicationPageService
.createOrUpdateSuffixedApplication(
existingApplication,
existingApplication.getName(),
0
);
}
throw error;
});
});
});
}
return applicationPageService.createOrUpdateSuffixedApplication(application, application.getName(), 0);
});
}
return parentApplicationMono
.flatMap(application1 -> {
// Set the policies from the defaultApplication
existingApplication.setPolicies(application1.getPolicies());
importedApplication.setPolicies(application1.getPolicies());
return applicationService.save(existingApplication)
.onErrorResume(DuplicateKeyException.class, error -> {
if (error.getMessage() != null) {
return applicationPageService
.createOrUpdateSuffixedApplication(
existingApplication,
existingApplication.getName(),
0
);
}
throw error;
});
});
});
}
return applicationPageService.createOrUpdateSuffixedApplication(application, application.getName(), 0);
});
}
)
.flatMap(savedApp -> importThemes(savedApp, importedDoc, appendToApp))
.flatMap(savedApp -> {

View File

@ -133,7 +133,7 @@ public class DatasourceTriggerSolutionTest {
Mockito
.when(datasourceStructureSolution
.getStructure(
(Datasource) Mockito.any(),
Mockito.anyString(),
Mockito.anyBoolean(), Mockito.any()))
.thenReturn(Mono.just(testStructure));
@ -141,8 +141,8 @@ public class DatasourceTriggerSolutionTest {
Mockito.doReturn(Mono.just(Boolean.TRUE)).when(featureFlagService).check(Mockito.any());
Mockito
.doReturn(Mono.zip(Mono.justOrEmpty(datasource),
Mono.just(new DatasourceContextIdentifier(datasourceId, null)),
Mono.just(new HashMap<>())))
Mono.just(new DatasourceContextIdentifier(datasourceId, null)),
Mono.just(new HashMap<>())))
.when(datasourceService).getEvaluatedDSAndDsContextKeyWithEnvMap(Mockito.any(Datasource.class), Mockito.any());
Mono<TriggerResultDTO> tableNameMono = datasourceTriggerSolution.trigger(