Merge branch 'release' of https://github.com/appsmithorg/appsmith into release
This commit is contained in:
commit
56f681dc16
|
|
@ -241,11 +241,9 @@ public class RestApiPlugin extends BasePlugin {
|
|||
));
|
||||
}
|
||||
|
||||
Object requestBodyAsObject = null;
|
||||
|
||||
if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
|
||||
try {
|
||||
requestBodyAsObject = objectFromJson(requestBodyAsString);
|
||||
objectFromJson(requestBodyAsString);
|
||||
} catch (JsonSyntaxException e) {
|
||||
return Mono.error(new AppsmithPluginException(
|
||||
AppsmithPluginError.PLUGIN_ERROR,
|
||||
|
|
@ -254,14 +252,10 @@ public class RestApiPlugin extends BasePlugin {
|
|||
}
|
||||
}
|
||||
|
||||
if (requestBodyAsObject == null) {
|
||||
requestBodyAsObject = requestBodyAsString;
|
||||
}
|
||||
|
||||
return webClient
|
||||
.method(httpMethod)
|
||||
.uri(uri)
|
||||
.body(BodyInserters.fromObject(requestBodyAsObject))
|
||||
.body(BodyInserters.fromObject(requestBodyAsString))
|
||||
.exchange()
|
||||
.doOnError(e -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)))
|
||||
.flatMap(response -> {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import lombok.Getter;
|
|||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
|
@ -29,4 +30,8 @@ public class Application extends BaseDomain {
|
|||
Boolean isPublic = false;
|
||||
|
||||
List<ApplicationPage> pages;
|
||||
|
||||
@Transient
|
||||
boolean appIsExample = false;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
|||
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
||||
|
||||
@Component
|
||||
public class PolicyUtils<T extends BaseDomain> {
|
||||
public class PolicyUtils {
|
||||
|
||||
private final PolicyGenerator policyGenerator;
|
||||
private final ApplicationRepository applicationRepository;
|
||||
|
|
@ -55,7 +55,7 @@ public class PolicyUtils<T extends BaseDomain> {
|
|||
this.datasourceRepository = datasourceRepository;
|
||||
}
|
||||
|
||||
public T addPoliciesToExistingObject(Map<String, Policy> policyMap, T obj) {
|
||||
public <T extends BaseDomain> T addPoliciesToExistingObject(Map<String, Policy> policyMap, T obj) {
|
||||
// Making a deep copy here so we don't modify the `policyMap` object.
|
||||
// TODO: Investigate a solution without using deep-copy.
|
||||
final Map<String, Policy> policyMap1 = new HashMap<>();
|
||||
|
|
@ -83,7 +83,7 @@ public class PolicyUtils<T extends BaseDomain> {
|
|||
return obj;
|
||||
}
|
||||
|
||||
public T removePoliciesFromExistingObject(Map<String, Policy> policyMap, T obj) {
|
||||
public <T extends BaseDomain> T removePoliciesFromExistingObject(Map<String, Policy> policyMap, T obj) {
|
||||
// Making a deep copy here so we don't modify the `policyMap` object.
|
||||
// TODO: Investigate a solution without using deep-copy.
|
||||
final Map<String, Policy> policyMap1 = new HashMap<>();
|
||||
|
|
@ -168,9 +168,9 @@ public class PolicyUtils<T extends BaseDomain> {
|
|||
.switchIfEmpty(Mono.empty())
|
||||
.map(datasource -> {
|
||||
if (addPolicyToObject) {
|
||||
return (Datasource) addPoliciesToExistingObject(newPoliciesMap, (T) datasource);
|
||||
return addPoliciesToExistingObject(newPoliciesMap, datasource);
|
||||
} else {
|
||||
return (Datasource) removePoliciesFromExistingObject(newPoliciesMap, (T) datasource);
|
||||
return removePoliciesFromExistingObject(newPoliciesMap, datasource);
|
||||
}
|
||||
})
|
||||
.collectList()
|
||||
|
|
@ -185,9 +185,9 @@ public class PolicyUtils<T extends BaseDomain> {
|
|||
.switchIfEmpty(Mono.empty())
|
||||
.map(application -> {
|
||||
if (addPolicyToObject) {
|
||||
return (Application) addPoliciesToExistingObject(newAppPoliciesMap, (T) application);
|
||||
return addPoliciesToExistingObject(newAppPoliciesMap, application);
|
||||
} else {
|
||||
return (Application) removePoliciesFromExistingObject(newAppPoliciesMap, (T) application);
|
||||
return removePoliciesFromExistingObject(newAppPoliciesMap, application);
|
||||
}
|
||||
})
|
||||
.collectList()
|
||||
|
|
@ -213,9 +213,9 @@ public class PolicyUtils<T extends BaseDomain> {
|
|||
.switchIfEmpty(Mono.empty())
|
||||
.map(page -> {
|
||||
if (addPolicyToObject) {
|
||||
return (Page) addPoliciesToExistingObject(newPagePoliciesMap, (T) page);
|
||||
return addPoliciesToExistingObject(newPagePoliciesMap, page);
|
||||
} else {
|
||||
return (Page) removePoliciesFromExistingObject(newPagePoliciesMap, (T) page);
|
||||
return removePoliciesFromExistingObject(newPagePoliciesMap, page);
|
||||
}
|
||||
})
|
||||
.collectList()
|
||||
|
|
@ -241,9 +241,9 @@ public class PolicyUtils<T extends BaseDomain> {
|
|||
.switchIfEmpty(Mono.empty())
|
||||
.map(action -> {
|
||||
if (addPolicyToObject) {
|
||||
return (Action) addPoliciesToExistingObject(newActionPoliciesMap, (T) action);
|
||||
return addPoliciesToExistingObject(newActionPoliciesMap, action);
|
||||
} else {
|
||||
return (Action) removePoliciesFromExistingObject(newActionPoliciesMap, (T) action);
|
||||
return removePoliciesFromExistingObject(newActionPoliciesMap, action);
|
||||
}
|
||||
})
|
||||
.map(action -> {
|
||||
|
|
|
|||
|
|
@ -19,4 +19,6 @@ public interface ActionRepository extends BaseRepository<Action, String>, Custom
|
|||
Mono<Long> countByDatasourceId(String datasourceId);
|
||||
|
||||
Flux<Action> findByPageId(String pageId);
|
||||
|
||||
Flux<Action> findByOrganizationId(String organizationId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
|
|||
private final PageRepository pageRepository;
|
||||
private final PolicyUtils policyUtils;
|
||||
private final DatasourceService datasourceService;
|
||||
private final ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
public ApplicationServiceImpl(Scheduler scheduler,
|
||||
|
|
@ -58,16 +59,18 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
|
|||
AnalyticsService analyticsService,
|
||||
PageRepository pageRepository,
|
||||
PolicyUtils policyUtils,
|
||||
DatasourceService datasourceService) {
|
||||
DatasourceService datasourceService,
|
||||
ConfigService configService) {
|
||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
||||
this.pageRepository = pageRepository;
|
||||
this.policyUtils = policyUtils;
|
||||
this.datasourceService = datasourceService;
|
||||
this.configService = configService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Application> get(MultiValueMap<String, String> params) {
|
||||
return super.getWithPermission(params, READ_APPLICATIONS);
|
||||
return setTransientFields(super.getWithPermission(params, READ_APPLICATIONS));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -82,32 +85,37 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
|
|||
|
||||
@Override
|
||||
public Mono<Application> findById(String id) {
|
||||
return repository.findById(id);
|
||||
return repository.findById(id)
|
||||
.flatMap(this::setTransientFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Application> findById(String id, AclPermission aclPermission) {
|
||||
return repository.findById(id, aclPermission);
|
||||
return repository.findById(id, aclPermission)
|
||||
.flatMap(this::setTransientFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Application> findByIdAndOrganizationId(String id, String organizationId, AclPermission permission) {
|
||||
return repository.findByIdAndOrganizationId(id, organizationId, permission);
|
||||
return repository.findByIdAndOrganizationId(id, organizationId, permission)
|
||||
.flatMap(this::setTransientFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Application> findByOrganizationId(String organizationId, AclPermission permission) {
|
||||
return repository.findByOrganizationId(organizationId, permission);
|
||||
return setTransientFields(repository.findByOrganizationId(organizationId, permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Application> findByName(String name, AclPermission permission) {
|
||||
return repository.findByName(name, permission);
|
||||
return repository.findByName(name, permission)
|
||||
.flatMap(this::setTransientFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Application> save(Application application) {
|
||||
return repository.save(application);
|
||||
return repository.save(application)
|
||||
.flatMap(this::setTransientFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -223,9 +231,9 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
|
|||
.map(datasource -> {
|
||||
Datasource updatedDatasource;
|
||||
if (isPublic) {
|
||||
updatedDatasource = (Datasource) policyUtils.addPoliciesToExistingObject(datasourcePolicyMap, datasource);
|
||||
updatedDatasource = policyUtils.addPoliciesToExistingObject(datasourcePolicyMap, datasource);
|
||||
} else {
|
||||
updatedDatasource = (Datasource) policyUtils.removePoliciesFromExistingObject(datasourcePolicyMap, datasource);
|
||||
updatedDatasource = policyUtils.removePoliciesFromExistingObject(datasourcePolicyMap, datasource);
|
||||
}
|
||||
|
||||
return datasourceService.save(updatedDatasource);
|
||||
|
|
@ -243,13 +251,28 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
|
|||
Application updatedApplication;
|
||||
|
||||
if (isPublic) {
|
||||
updatedApplication = (Application) policyUtils.addPoliciesToExistingObject(applicationPolicyMap, (Application) application);
|
||||
updatedApplication = policyUtils.addPoliciesToExistingObject(applicationPolicyMap, application);
|
||||
} else {
|
||||
updatedApplication = (Application) policyUtils.removePoliciesFromExistingObject(applicationPolicyMap, (Application) application);
|
||||
updatedApplication = policyUtils.removePoliciesFromExistingObject(applicationPolicyMap, application);
|
||||
}
|
||||
|
||||
return repository.save(updatedApplication);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<Application> setTransientFields(Application application) {
|
||||
return setTransientFields(Flux.just(application)).last();
|
||||
}
|
||||
|
||||
private Flux<Application> setTransientFields(Flux<Application> applicationsFlux) {
|
||||
return configService.getTemplateOrganizationId()
|
||||
.defaultIfEmpty("")
|
||||
.cache()
|
||||
.repeat()
|
||||
.zipWith(applicationsFlux, (templateOrganizationId, application) -> {
|
||||
application.setAppIsExample(templateOrganizationId.equals(application.getOrganizationId()));
|
||||
return application;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ public interface ConfigService extends CrudService<Config, String> {
|
|||
Mono<Config> getByName(String name);
|
||||
|
||||
Mono<Config> updateByName(String name, Config config);
|
||||
|
||||
Mono<String> getTemplateOrganizationId();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import javax.validation.Validator;
|
|||
@Service
|
||||
public class ConfigServiceImpl extends BaseService<ConfigRepository, Config, String> implements ConfigService {
|
||||
|
||||
private static final String TEMPLATE_ORGANIZATION_CONFIG_NAME = "template-organization";
|
||||
|
||||
public ConfigServiceImpl(Scheduler scheduler,
|
||||
Validator validator,
|
||||
MongoConverter mongoConverter,
|
||||
|
|
@ -43,4 +45,10 @@ public class ConfigServiceImpl extends BaseService<ConfigRepository, Config, Str
|
|||
return repository.save(dbConfig);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> getTemplateOrganizationId() {
|
||||
return repository.findByName(TEMPLATE_ORGANIZATION_CONFIG_NAME)
|
||||
.map(config -> config.getConfig().getAsString(FieldName.ORGANIZATION_ID));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ public class UserOrganizationServiceImpl implements UserOrganizationService {
|
|||
Map<String, Policy> actionPolicyMap = policyUtils.generateActionPoliciesFromPagePolicies(pagePolicyMap);
|
||||
|
||||
//Now update the organization policies
|
||||
Organization updatedOrganization = (Organization) policyUtils.addPoliciesToExistingObject(orgPolicyMap, organization);
|
||||
Organization updatedOrganization = policyUtils.addPoliciesToExistingObject(orgPolicyMap, organization);
|
||||
updatedOrganization.setUserRoles(userRoles);
|
||||
|
||||
// Update the underlying application/page/action
|
||||
|
|
@ -221,7 +221,7 @@ public class UserOrganizationServiceImpl implements UserOrganizationService {
|
|||
Map<String, Policy> actionPolicyMap = policyUtils.generateActionPoliciesFromPagePolicies(pagePolicyMap);
|
||||
|
||||
//Now update the organization policies
|
||||
Organization updatedOrganization = (Organization) policyUtils.removePoliciesFromExistingObject(orgPolicyMap, organization);
|
||||
Organization updatedOrganization = policyUtils.removePoliciesFromExistingObject(orgPolicyMap, organization);
|
||||
updatedOrganization.setUserRoles(userRoles);
|
||||
|
||||
// Update the underlying application/page/action
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
|||
Set<AclPermission> invitePermissions = inviteUser.getRole().getPermissions();
|
||||
// Append the permissions to the application and return
|
||||
Map<String, Policy> policyMap = policyUtils.generatePolicyFromPermission(invitePermissions, inviteUser);
|
||||
return (Application) policyUtils.addPoliciesToExistingObject(policyMap, application);
|
||||
return policyUtils.addPoliciesToExistingObject(policyMap, application);
|
||||
|
||||
// Append the required permissions to all the pages
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
package com.appsmith.server.solutions;
|
||||
|
||||
import com.appsmith.external.models.BaseDomain;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.Action;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.Datasource;
|
||||
import com.appsmith.server.domains.Layout;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.domains.Page;
|
||||
import com.appsmith.server.domains.User;
|
||||
import com.appsmith.server.dtos.DslActionDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import com.appsmith.server.repositories.ActionRepository;
|
||||
import com.appsmith.server.repositories.ApplicationRepository;
|
||||
import com.appsmith.server.repositories.ConfigRepository;
|
||||
import com.appsmith.server.repositories.DatasourceRepository;
|
||||
import com.appsmith.server.repositories.OrganizationRepository;
|
||||
import com.appsmith.server.repositories.PageRepository;
|
||||
import com.appsmith.server.services.ActionService;
|
||||
import com.appsmith.server.services.ApplicationPageService;
|
||||
import com.appsmith.server.services.ConfigService;
|
||||
import com.appsmith.server.services.DatasourceContextService;
|
||||
import com.appsmith.server.services.DatasourceService;
|
||||
import com.appsmith.server.services.OrganizationService;
|
||||
|
|
@ -25,21 +26,23 @@ import com.appsmith.server.services.SessionUserService;
|
|||
import com.appsmith.server.services.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ExamplesOrganizationCloner {
|
||||
|
||||
public static final String TEMPLATE_ORGANIZATION_CONFIG_NAME = "template-organization";
|
||||
|
||||
private final OrganizationService organizationService;
|
||||
private final OrganizationRepository organizationRepository;
|
||||
private final DatasourceService datasourceService;
|
||||
|
|
@ -48,7 +51,7 @@ public class ExamplesOrganizationCloner {
|
|||
private final DatasourceRepository datasourceRepository;
|
||||
private final ApplicationRepository applicationRepository;
|
||||
private final ActionRepository actionRepository;
|
||||
private final ConfigRepository configRepository;
|
||||
private final ConfigService configService;
|
||||
private final SessionUserService sessionUserService;
|
||||
private final UserService userService;
|
||||
private final ApplicationPageService applicationPageService;
|
||||
|
|
@ -64,6 +67,7 @@ public class ExamplesOrganizationCloner {
|
|||
* Clones the template organization (as specified in config collection) for the given user. The given user will be
|
||||
* the owner of the cloned organization. This method also assumes that the given user is the same as the user in
|
||||
* the current Spring session.
|
||||
*
|
||||
* @param user User who will be the owner of the cloned organization.
|
||||
* @return Empty Mono.
|
||||
*/
|
||||
|
|
@ -73,29 +77,27 @@ public class ExamplesOrganizationCloner {
|
|||
return Mono.empty();
|
||||
}
|
||||
|
||||
return configRepository.findByName(TEMPLATE_ORGANIZATION_CONFIG_NAME)
|
||||
return configService.getTemplateOrganizationId()
|
||||
.doOnSuccess(config -> {
|
||||
if (config == null) {
|
||||
// If the template organization could not be found, that's okay, the login should not fail. We
|
||||
// will try again the next time the user logs in.
|
||||
log.error(
|
||||
"Couldn't find config by name {}. Skipping creating example organization for user {}.",
|
||||
TEMPLATE_ORGANIZATION_CONFIG_NAME,
|
||||
"Template organization ID not found. Skipping creating example organization for user {}.",
|
||||
user.getEmail()
|
||||
);
|
||||
}
|
||||
})
|
||||
.flatMap(config ->
|
||||
cloneOrganizationForUser(config.getConfig().getAsString(FieldName.ORGANIZATION_ID), user)
|
||||
);
|
||||
.flatMap(templateOrganizationId -> cloneOrganizationForUser(templateOrganizationId, user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an organization ID and a user, clone the organization and make the given user the owner of the cloned
|
||||
* organization. This recursively clones all objects inside the organization. This method also assumes that the
|
||||
* given user is the same as the user in the current Spring session.
|
||||
*
|
||||
* @param templateOrganizationId Organization ID of the organization to create a clone of.
|
||||
* @param user The user who will own the new cloned organization.
|
||||
* @param user The user who will own the new cloned organization.
|
||||
* @return Publishes the newly created organization.
|
||||
*/
|
||||
public Mono<Organization> cloneOrganizationForUser(String templateOrganizationId, User user) {
|
||||
|
|
@ -139,12 +141,15 @@ public class ExamplesOrganizationCloner {
|
|||
/**
|
||||
* Clone all applications (except deleted ones), including it's pages and actions from one organization into
|
||||
* another. Also clones all datasources (not just the ones used by any applications) in the given organizations.
|
||||
*
|
||||
* @param fromOrganizationId ID of the organization that is the source to copy objects from.
|
||||
* @param toOrganizationId ID of the organization that is the target to copy objects to.
|
||||
* @param toOrganizationId ID of the organization that is the target to copy objects to.
|
||||
* @return Empty Mono.
|
||||
*/
|
||||
private Mono<Void> cloneApplications(String fromOrganizationId, String toOrganizationId) {
|
||||
final Mono<Map<String, Datasource>> cloneDatasourcesMono = cloneDatasources(fromOrganizationId, toOrganizationId).cache();
|
||||
final List<Page> clonedPages = new ArrayList<>();
|
||||
|
||||
return applicationRepository
|
||||
.findByOrganizationIdAndIsPublicTrue(fromOrganizationId)
|
||||
.flatMap(application -> {
|
||||
|
|
@ -155,28 +160,27 @@ public class ExamplesOrganizationCloner {
|
|||
.flatMap(page -> {
|
||||
final String templatePageId = page.getId();
|
||||
makePristine(page);
|
||||
if (page.getLayouts() != null) {
|
||||
for (final Layout layout : page.getLayouts()) {
|
||||
layout.setId(new ObjectId().toString());
|
||||
}
|
||||
}
|
||||
return applicationPageService
|
||||
.createPage(page)
|
||||
.flatMap(page1 -> {
|
||||
log.info("Cloned into new page {}", page1);
|
||||
return applicationRepository.findById(page.getApplicationId())
|
||||
.map(application -> {
|
||||
log.info("Application after page got cloned: {}", application);
|
||||
return page1;
|
||||
.flatMapMany(savedPage -> {
|
||||
clonedPages.add(savedPage);
|
||||
return actionRepository
|
||||
.findByPageId(templatePageId)
|
||||
.map(action -> {
|
||||
log.info("Preparing action for cloning {} {}.", action.getName(), action.getId());
|
||||
action.setPageId(savedPage.getId());
|
||||
return action;
|
||||
});
|
||||
})
|
||||
.flatMapMany(
|
||||
savedPage -> actionRepository
|
||||
.findByPageId(templatePageId)
|
||||
.map(action -> {
|
||||
log.info("Preparing action for cloning {} {}.", action.getName(), action.getId());
|
||||
action.setPageId(savedPage.getId());
|
||||
return action;
|
||||
})
|
||||
);
|
||||
});
|
||||
})
|
||||
.flatMap(action -> {
|
||||
log.info("Creating clone of action {}", action.getId());
|
||||
final String originalActionId = action.getId();
|
||||
log.info("Creating clone of action {}", originalActionId);
|
||||
makePristine(action);
|
||||
action.setOrganizationId(toOrganizationId);
|
||||
action.setCollectionId(null);
|
||||
|
|
@ -193,7 +197,53 @@ public class ExamplesOrganizationCloner {
|
|||
datasourceInsideAction.setOrganizationId(toOrganizationId);
|
||||
}
|
||||
}
|
||||
return actionMono.flatMap(actionService::create);
|
||||
return actionMono
|
||||
.flatMap(actionService::create)
|
||||
.map(Action::getId)
|
||||
.zipWith(Mono.just(originalActionId));
|
||||
})
|
||||
// This call to `collectMap` will wait for all actions in all pages to have been processed, and so the
|
||||
// `clonedPages` list will also contain all pages cloned.
|
||||
.collectMap(Tuple2::getT2, Tuple2::getT1)
|
||||
.flatMapMany(actionIdsMap -> {
|
||||
final List<Mono<Page>> pageSaveMonos = new ArrayList<>();
|
||||
|
||||
for (final Page page : clonedPages) {
|
||||
if (page.getLayouts() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean shouldSave = false;
|
||||
|
||||
for (final Layout layout : page.getLayouts()) {
|
||||
if (layout.getLayoutOnLoadActions() != null) {
|
||||
for (final Set<DslActionDTO> actionSet : layout.getLayoutOnLoadActions()) {
|
||||
for (final DslActionDTO actionDTO : actionSet) {
|
||||
if (actionIdsMap.containsKey(actionDTO.getId())) {
|
||||
actionDTO.setId(actionIdsMap.get(actionDTO.getId()));
|
||||
shouldSave = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layout.getPublishedLayoutOnLoadActions() != null) {
|
||||
for (final Set<DslActionDTO> actionSet : layout.getPublishedLayoutOnLoadActions()) {
|
||||
for (final DslActionDTO actionDTO : actionSet) {
|
||||
if (actionIdsMap.containsKey(actionDTO.getId())) {
|
||||
actionDTO.setId(actionIdsMap.get(actionDTO.getId()));
|
||||
shouldSave = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSave) {
|
||||
pageSaveMonos.add(pageRepository.save(page));
|
||||
}
|
||||
}
|
||||
|
||||
return Flux.concat(pageSaveMonos);
|
||||
})
|
||||
.then(cloneDatasourcesMono) // Run the datasource cloning mono if it isn't already done.
|
||||
.then();
|
||||
|
|
@ -217,8 +267,9 @@ public class ExamplesOrganizationCloner {
|
|||
* Clone all the datasources (except deleted ones) from one organization to another. Publishes a map where the keys
|
||||
* are IDs of datasources that were copied (source IDs), and the values are the cloned datasource objects which
|
||||
* contain the new ID.
|
||||
*
|
||||
* @param fromOrganizationId ID of the organization that is the source to copy datasources from.
|
||||
* @param toOrganizationId ID of the organization that is the target to copy datasources to.
|
||||
* @param toOrganizationId ID of the organization that is the target to copy datasources to.
|
||||
* @return Mono of a mapping with old datasource IDs as keys and new datasource objects as values.
|
||||
*/
|
||||
private Mono<Map<String, Datasource>> cloneDatasources(String fromOrganizationId, String toOrganizationId) {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ public class ApplicationServiceTest {
|
|||
.create(applicationMono)
|
||||
.assertNext(application -> {
|
||||
assertThat(application).isNotNull();
|
||||
assertThat(application.isAppIsExample()).isFalse();
|
||||
assertThat(application.getId()).isNotNull();
|
||||
assertThat(application.getName().equals("ApplicationServiceTest TestApp"));
|
||||
assertThat(application.getPolicies()).isNotEmpty();
|
||||
|
|
@ -201,6 +202,7 @@ public class ApplicationServiceTest {
|
|||
StepVerifier.create(getApplication)
|
||||
.assertNext(t -> {
|
||||
assertThat(t).isNotNull();
|
||||
assertThat(t.isAppIsExample()).isFalse();
|
||||
assertThat(t.getId()).isNotNull();
|
||||
assertThat(t.getName()).isEqualTo("validGetApplicationById-Test");
|
||||
})
|
||||
|
|
@ -221,6 +223,7 @@ public class ApplicationServiceTest {
|
|||
StepVerifier.create(getApplication)
|
||||
.assertNext(t -> {
|
||||
assertThat(t).isNotNull();
|
||||
assertThat(t.isAppIsExample()).isFalse();
|
||||
assertThat(t.getId()).isNotNull();
|
||||
assertThat(t.getName()).isEqualTo("validGetApplicationByName-Test");
|
||||
})
|
||||
|
|
@ -248,6 +251,7 @@ public class ApplicationServiceTest {
|
|||
.filter(t -> t.getName().equals("validGetApplications-Test"))
|
||||
.forEach(t -> {
|
||||
assertThat(t.getId()).isNotNull();
|
||||
assertThat(t.isAppIsExample()).isFalse();
|
||||
assertThat(t.getPolicies()).isNotEmpty();
|
||||
assertThat(t.getPolicies()).containsAll(Set.of(readAppPolicy));
|
||||
});
|
||||
|
|
@ -329,6 +333,7 @@ public class ApplicationServiceTest {
|
|||
|
||||
Application application = orgAppDto.getApplications().get(0);
|
||||
assertThat(application.getUserPermissions()).contains("read:applications");
|
||||
assertThat(application.isAppIsExample()).isFalse();
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
package com.appsmith.server.solutions;
|
||||
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.services.ApplicationPageService;
|
||||
import com.appsmith.server.services.ApplicationService;
|
||||
import com.appsmith.server.services.ConfigService;
|
||||
import com.appsmith.server.services.OrganizationService;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.services.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.security.test.context.support.WithUserDetails;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@Slf4j
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@DirtiesContext
|
||||
public class ExampleApplicationsAreMarked {
|
||||
|
||||
@Autowired
|
||||
UserService userService;
|
||||
|
||||
@Autowired
|
||||
private ApplicationService applicationService;
|
||||
|
||||
@Autowired
|
||||
private OrganizationService organizationService;
|
||||
|
||||
@Autowired
|
||||
private ApplicationPageService applicationPageService;
|
||||
|
||||
@Autowired
|
||||
private SessionUserService sessionUserService;
|
||||
|
||||
@MockBean
|
||||
private ConfigService configService;
|
||||
|
||||
@Test
|
||||
@WithUserDetails(value = "api_user")
|
||||
public void exampleApplicationsAreMarked() {
|
||||
Organization newOrganization = new Organization();
|
||||
newOrganization.setName("Template Organization 3");
|
||||
final Mono<List<Application>> resultMono = Mono
|
||||
.zip(
|
||||
organizationService.create(newOrganization),
|
||||
sessionUserService.getCurrentUser()
|
||||
)
|
||||
.flatMap(tuple -> {
|
||||
final Organization organization = tuple.getT1();
|
||||
|
||||
Mockito.when(configService.getTemplateOrganizationId()).thenReturn(Mono.just(organization.getId()));
|
||||
|
||||
final Application app1 = new Application();
|
||||
app1.setName("first application");
|
||||
app1.setOrganizationId(organization.getId());
|
||||
app1.setIsPublic(true);
|
||||
|
||||
final Application app2 = new Application();
|
||||
app2.setName("second application");
|
||||
app2.setOrganizationId(organization.getId());
|
||||
app2.setIsPublic(true);
|
||||
|
||||
final Application app3 = new Application();
|
||||
app3.setName("third application");
|
||||
app3.setOrganizationId(organization.getId());
|
||||
app3.setIsPublic(false);
|
||||
|
||||
return Mono
|
||||
.when(
|
||||
applicationPageService.createApplication(app1),
|
||||
applicationPageService.createApplication(app2),
|
||||
applicationPageService.createApplication(app3)
|
||||
)
|
||||
.thenReturn(organization.getId());
|
||||
})
|
||||
.flatMapMany(organizationId -> applicationService.findByOrganizationId(organizationId, READ_APPLICATIONS))
|
||||
.collectList();
|
||||
|
||||
StepVerifier.create(resultMono)
|
||||
.assertNext(applications -> {
|
||||
assertThat(applications).hasSize(3);
|
||||
assertThat(applications.stream().allMatch(Application::isAppIsExample)).isTrue();
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.appsmith.server.solutions;
|
||||
|
||||
import com.appsmith.external.models.ActionConfiguration;
|
||||
import com.appsmith.external.models.AuthenticationDTO;
|
||||
import com.appsmith.external.models.DatasourceConfiguration;
|
||||
import com.appsmith.external.models.Property;
|
||||
|
|
@ -7,9 +8,11 @@ import com.appsmith.server.constants.FieldName;
|
|||
import com.appsmith.server.domains.Action;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.Datasource;
|
||||
import com.appsmith.server.domains.Layout;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.domains.Page;
|
||||
import com.appsmith.server.domains.Plugin;
|
||||
import com.appsmith.server.dtos.DslActionDTO;
|
||||
import com.appsmith.server.helpers.MockPluginExecutor;
|
||||
import com.appsmith.server.helpers.PluginExecutorHelper;
|
||||
import com.appsmith.server.repositories.PluginRepository;
|
||||
|
|
@ -19,11 +22,14 @@ import com.appsmith.server.services.ApplicationPageService;
|
|||
import com.appsmith.server.services.ApplicationService;
|
||||
import com.appsmith.server.services.DatasourceService;
|
||||
import com.appsmith.server.services.EncryptionService;
|
||||
import com.appsmith.server.services.LayoutActionService;
|
||||
import com.appsmith.server.services.OrganizationService;
|
||||
import com.appsmith.server.services.PageService;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.services.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.minidev.json.JSONObject;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -31,6 +37,10 @@ import org.mockito.Mockito;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.test.context.support.WithUserDetails;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
|
@ -41,6 +51,7 @@ import reactor.test.StepVerifier;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
|
@ -96,6 +107,12 @@ public class ExamplesOrganizationClonerTests {
|
|||
@MockBean
|
||||
private PluginExecutorHelper pluginExecutorHelper;
|
||||
|
||||
@Autowired
|
||||
private LayoutActionService layoutActionService;
|
||||
|
||||
@Autowired
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
private Plugin installedPlugin;
|
||||
|
||||
private static class OrganizationData {
|
||||
|
|
@ -485,6 +502,27 @@ public class ExamplesOrganizationClonerTests {
|
|||
final String pageId1 = app.getPages().get(0).getId();
|
||||
final Datasource ds1Again = tuple1.getT3();
|
||||
|
||||
final Page newPage = new Page();
|
||||
newPage.setName("A New Page");
|
||||
newPage.setApplicationId(app.getId());
|
||||
newPage.setLayouts(new ArrayList<>());
|
||||
final Layout layout = new Layout();
|
||||
layout.setId(new ObjectId().toString());
|
||||
layout.setDsl(new JSONObject(Map.of("text", "draft {{ newPageAction.data }}")));
|
||||
layout.setPublishedDsl(new JSONObject(Map.of("text", "published {{ newPageAction.data }}")));
|
||||
final DslActionDTO actionDTO = new DslActionDTO();
|
||||
final HashSet<DslActionDTO> dslActionDTOS = new HashSet<>(List.of(actionDTO));
|
||||
layout.setLayoutOnLoadActions(List.of(dslActionDTOS));
|
||||
newPage.getLayouts().add(layout);
|
||||
|
||||
final Action newPageAction = new Action();
|
||||
newPageAction.setName("newPageAction");
|
||||
newPageAction.setOrganizationId(organization.getId());
|
||||
newPageAction.setDatasource(ds1Again);
|
||||
newPageAction.setPluginId(installedPlugin.getId());
|
||||
newPageAction.setActionConfiguration(new ActionConfiguration());
|
||||
newPageAction.getActionConfiguration().setHttpMethod(HttpMethod.GET);
|
||||
|
||||
final Action action1 = new Action();
|
||||
action1.setName("action1");
|
||||
action1.setPageId(pageId1);
|
||||
|
|
@ -518,6 +556,20 @@ public class ExamplesOrganizationClonerTests {
|
|||
action4.setPluginId(installedPlugin.getId());
|
||||
|
||||
return Mono.when(
|
||||
applicationPageService.createPage(newPage)
|
||||
.flatMap(page -> {
|
||||
newPageAction.setPageId(page.getId());
|
||||
return applicationPageService.addPageToApplication(app, page, false)
|
||||
.then(actionCollectionService.createAction(newPageAction))
|
||||
.flatMap(savedAction -> {
|
||||
return layoutActionService.updateAction(savedAction.getId(), savedAction);
|
||||
})
|
||||
.then(pageService.findById(page.getId(), READ_PAGES));
|
||||
})
|
||||
.map(tuple2 -> {
|
||||
log.info("Created action and added page to app {}", tuple2);
|
||||
return tuple2;
|
||||
}),
|
||||
actionCollectionService.createAction(action1),
|
||||
actionCollectionService.createAction(action2),
|
||||
actionCollectionService.createAction(action3),
|
||||
|
|
@ -526,6 +578,9 @@ public class ExamplesOrganizationClonerTests {
|
|||
})
|
||||
.then(examplesOrganizationCloner.cloneOrganizationForUser(organization.getId(), tuple.getT2()));
|
||||
})
|
||||
.doOnError(error -> {
|
||||
log.error("Error preparing data for test", error);
|
||||
})
|
||||
.flatMap(this::loadOrganizationData);
|
||||
|
||||
StepVerifier.create(resultMono)
|
||||
|
|
@ -541,14 +596,21 @@ public class ExamplesOrganizationClonerTests {
|
|||
"second application"
|
||||
);
|
||||
|
||||
final Application firstApplication = data.applications.stream().filter(app -> app.getName().equals("first application")).findFirst().get();
|
||||
final Page newPage = mongoTemplate.findOne(Query.query(Criteria.where("applicationId").is(firstApplication.getId()).and("name").is("A New Page")), Page.class);
|
||||
final String actionId = newPage.getLayouts().get(0).getLayoutOnLoadActions().get(0).iterator().next().getId();
|
||||
final Action newPageAction = mongoTemplate.findOne(Query.query(Criteria.where("id").is(actionId)), Action.class);
|
||||
assertThat(newPageAction.getOrganizationId()).isEqualTo(data.organization.getId());
|
||||
|
||||
assertThat(data.datasources).hasSize(2);
|
||||
assertThat(map(data.datasources, Datasource::getName)).containsExactlyInAnyOrder(
|
||||
"datasource 1",
|
||||
"datasource 2"
|
||||
);
|
||||
|
||||
assertThat(data.actions).hasSize(4);
|
||||
assertThat(data.actions).hasSize(5);
|
||||
assertThat(map(data.actions, Action::getName)).containsExactlyInAnyOrder(
|
||||
"newPageAction",
|
||||
"action1",
|
||||
"action2",
|
||||
"action3",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user