Add transient field on applications to indicate whether it's an example application (#256)

* Add transient field on application to indicate whether it's an example

* Add test for marking of example applications

* Remove unused constant
This commit is contained in:
Shrikant Sharat Kandula 2020-08-11 14:00:41 +05:30 committed by GitHub
parent dc10f09906
commit 0c344115c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 20 deletions

View File

@ -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;
}

View File

@ -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
@ -252,4 +260,19 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
});
}
}
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;
});
}
}

View File

@ -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();
}

View File

@ -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));
}
}

View File

@ -1,7 +1,6 @@
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;
@ -14,12 +13,12 @@ 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;
@ -44,8 +43,6 @@ import java.util.Set;
@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;
@ -54,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;
@ -80,21 +77,18 @@ 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));
}
/**

View File

@ -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();

View File

@ -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();
}
}