Merge branch 'feature/page-crud' into 'master'

CRUD for pages and layouts.

Closes #33

See merge request theappsmith/internal-tools-server!14
This commit is contained in:
Trisha Anand 2019-09-11 10:44:31 +00:00
commit e6ef59295a
19 changed files with 769 additions and 20 deletions

View File

@ -7,7 +7,6 @@ import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.services.OrganizationService; import com.appsmith.server.services.OrganizationService;
import com.appsmith.server.services.UserService; import com.appsmith.server.services.UserService;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
@ -84,10 +83,10 @@ public class ClientUserRepository implements ServerOAuth2AuthorizedClientReposit
// Check if the list of configured custom domains match the authenticated principal. // Check if the list of configured custom domains match the authenticated principal.
// This is to provide more control over which accounts can be used to access the application. // This is to provide more control over which accounts can be used to access the application.
// TODO: This is not a good way to do this. Ideally, we should pass "hd=example.com" to OAuth2 provider to list relevant accounts only // TODO: This is not a good way to do this. Ideally, we should pass "hd=example.com" to OAuth2 provider to list relevant accounts only
if(!commonConfig.getAllowedDomains().isEmpty()) { if (!commonConfig.getAllowedDomains().isEmpty()) {
DefaultOidcUser userPrincipal = (DefaultOidcUser) principal.getPrincipal(); DefaultOidcUser userPrincipal = (DefaultOidcUser) principal.getPrincipal();
String domain = (String) userPrincipal.getAttributes().getOrDefault("hd", ""); String domain = (String) userPrincipal.getAttributes().getOrDefault("hd", "");
if(!commonConfig.getAllowedDomains().contains(domain)) { if (!commonConfig.getAllowedDomains().contains(domain)) {
return Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_DOMAIN)); return Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_DOMAIN));
} }
} }

View File

@ -4,4 +4,7 @@ public class FieldName {
public static String ORGANIZATION = "organization"; public static String ORGANIZATION = "organization";
public static String ID = "id"; public static String ID = "id";
public static String NAME = "name"; public static String NAME = "name";
public static String PAGEID = "pageId";
public static String LAYOUTID = "layoutId";
public static String APPLICATIONID = "applicationId";
} }

View File

@ -13,4 +13,5 @@ public interface Url {
String ACTION_URL = BASE_URL + VERSION + "/actions"; String ACTION_URL = BASE_URL + VERSION + "/actions";
String USER_URL = BASE_URL + VERSION + "/users"; String USER_URL = BASE_URL + VERSION + "/users";
String APPLICATION_URL = BASE_URL + VERSION + "/applications"; String APPLICATION_URL = BASE_URL + VERSION + "/applications";
String PAGE_URL = BASE_URL + VERSION + "/pages";
} }

View File

@ -2,17 +2,48 @@ package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url; import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.Layout;
import com.appsmith.server.dtos.ResponseDto;
import com.appsmith.server.services.LayoutService; import com.appsmith.server.services.LayoutService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import javax.validation.Valid;
@RestController @RestController
@RequestMapping(Url.LAYOUT_URL) @RequestMapping(Url.LAYOUT_URL)
public class LayoutController extends BaseController<LayoutService, Layout, String> { public class LayoutController {
private final LayoutService service;
@Autowired @Autowired
public LayoutController(LayoutService service) { public LayoutController(LayoutService layoutService) {
super(service); this.service = layoutService;
} }
@PostMapping("/pages/{pageId}")
public Mono<ResponseDto<Layout>> createLayout(@PathVariable String pageId, @Valid @RequestBody Layout layout) {
return service.createLayout(pageId, layout)
.map(created -> new ResponseDto<>(HttpStatus.CREATED.value(), created, null));
}
@GetMapping("/{layoutId}/pages/{pageId}")
public Mono<ResponseDto<Layout>> getLayout(@PathVariable String pageId, @PathVariable String layoutId) {
return service.getLayout(pageId, layoutId)
.map(created -> new ResponseDto<>(HttpStatus.CREATED.value(), created, null));
}
@PutMapping("/{layoutId}/pages/{pageId}")
public Mono<ResponseDto<Layout>> updateLayout(@PathVariable String pageId, @PathVariable String layoutId, @RequestBody Layout layout) {
return service.updateLayout(pageId, layoutId, layout)
.map(created -> new ResponseDto<>(HttpStatus.CREATED.value(), created, null));
}
} }

View File

@ -0,0 +1,21 @@
package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Page;
import com.appsmith.server.services.PageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(Url.PAGE_URL)
@Slf4j
public class PageController extends BaseController<PageService, Page, String> {
@Autowired
public PageController(PageService service) {
super(service);
}
}

View File

@ -6,6 +6,7 @@ import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Document;
import javax.validation.constraints.NotNull;
import java.util.List; import java.util.List;
@Getter @Getter
@ -16,6 +17,7 @@ import java.util.List;
public class Page extends BaseDomain { public class Page extends BaseDomain {
String name; String name;
@NotNull
String applicationId; String applicationId;
List<Layout> layouts; List<Layout> layouts;

View File

@ -12,6 +12,7 @@ public enum AppsmithError {
PLUGIN_NOT_INSTALLED(400, 4001, "Plugin {0} not installed"), PLUGIN_NOT_INSTALLED(400, 4001, "Plugin {0} not installed"),
PLUGIN_ID_NOT_GIVEN(400, 4002, "Missing plugin id. Please input correct plugin id"), PLUGIN_ID_NOT_GIVEN(400, 4002, "Missing plugin id. Please input correct plugin id"),
RESOURCE_ID_NOT_GIVEN(400, 4003, "Missing resource id. Please input correct resource id"), RESOURCE_ID_NOT_GIVEN(400, 4003, "Missing resource id. Please input correct resource id"),
PAGE_DOESNT_BELONG_TO_USER_ORGANIZATION(400, 4006, "Page {0} does not belong to the current user {1} organization."),
UNAUTHORIZED_DOMAIN(401, 4001, "Invalid email domain provided. Please sign in with a valid work email ID"), UNAUTHORIZED_DOMAIN(401, 4001, "Invalid email domain provided. Please sign in with a valid work email ID"),
INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request"); INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request");

View File

@ -2,7 +2,16 @@ package com.appsmith.server.repositories;
import com.appsmith.server.domains.Application; import com.appsmith.server.domains.Application;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Repository @Repository
public interface ApplicationRepository extends BaseRepository<Application, String> { public interface ApplicationRepository extends BaseRepository<Application, String> {
Flux<Application> findByOrganizationId(String orgId);
Mono<Application> findByIdAndOrganizationId(String id, String orgId);
Mono<Application> findById(String id);
Mono<Application> findByName(String name);
} }

View File

@ -0,0 +1,14 @@
package com.appsmith.server.repositories;
import com.appsmith.server.domains.Page;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
@Repository
public interface PageRepository extends BaseRepository<Page, String> {
Mono<Page> findByIdAndLayoutsId(String id, String layoutId);
Mono<Page> findByName(String name);
Mono<Void> deleteAll();
}

View File

@ -1,6 +1,12 @@
package com.appsmith.server.services; package com.appsmith.server.services;
import com.appsmith.server.domains.Application; import com.appsmith.server.domains.Application;
import reactor.core.publisher.Mono;
public interface ApplicationService extends CrudService<Application, String> { public interface ApplicationService extends CrudService<Application, String> {
Mono<Application> findById(String id);
Mono<Application> findByIdAndOrganizationId(String id, String organizationId);
Mono<Application> findByName(String name);
} }

View File

@ -1,18 +1,23 @@
package com.appsmith.server.services; package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application; import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.User; import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.ApplicationRepository; import com.appsmith.server.repositories.ApplicationRepository;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Scheduler;
import javax.validation.Validator; import javax.validation.Validator;
@Slf4j @Slf4j
@Service @Service
public class ApplicationServiceImpl extends BaseService<ApplicationRepository, Application, String> implements ApplicationService { public class ApplicationServiceImpl extends BaseService<ApplicationRepository, Application, String> implements ApplicationService {
@ -27,6 +32,10 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
@Override @Override
public Mono<Application> create(Application application) { public Mono<Application> create(Application application) {
if (application.getName() == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.NAME));
}
Mono<User> userMono = userService.getCurrentUser(); Mono<User> userMono = userService.getCurrentUser();
return userMono return userMono
@ -37,4 +46,42 @@ public class ApplicationServiceImpl extends BaseService<ApplicationRepository, A
}) })
.flatMap(repository::save); .flatMap(repository::save);
} }
@Override
public Flux<Application> get() {
Mono<User> userMono = userService.getCurrentUser();
return userMono
.map(user -> user.getOrganizationId())
.flatMapMany(orgId -> repository.findByOrganizationId(orgId));
}
@Override
public Mono<Application> getById(String id) {
if (id == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
}
Mono<User> userMono = userService.getCurrentUser();
return userMono
.map(user -> user.getOrganizationId())
.flatMap(orgId -> repository.findByIdAndOrganizationId(id, orgId))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "resource", id)));
}
@Override
public Mono<Application> findById(String id) {
return repository.findById(id);
}
@Override
public Mono<Application> findByIdAndOrganizationId(String id, String organizationId) {
return repository.findByIdAndOrganizationId(id, organizationId);
}
@Override
public Mono<Application> findByName(String name) {
return repository.findByName(name);
}
} }

View File

@ -1,6 +1,12 @@
package com.appsmith.server.services; package com.appsmith.server.services;
import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.Layout;
import reactor.core.publisher.Mono;
public interface LayoutService extends CrudService<Layout, String> { public interface LayoutService {
Mono<Layout> createLayout(String pageId, Layout layout);
Mono<Layout> getLayout(String pageId, String layoutId);
Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout);
} }

View File

@ -1,26 +1,100 @@
package com.appsmith.server.services; package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.Layout;
import com.appsmith.server.repositories.LayoutRepository; import com.appsmith.server.domains.Page;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import reactor.core.scheduler.Scheduler; import reactor.core.publisher.Mono;
import javax.validation.Validator; import java.util.ArrayList;
import java.util.List;
@Slf4j @Slf4j
@Service @Service
public class LayoutServiceImpl extends BaseService<LayoutRepository, Layout, String> implements LayoutService { public class LayoutServiceImpl implements LayoutService {
private final PageService pageService;
@Autowired @Autowired
public LayoutServiceImpl(Scheduler scheduler, public LayoutServiceImpl(PageService pageService) {
Validator validator, this.pageService = pageService;
MongoConverter mongoConverter, }
ReactiveMongoTemplate reactiveMongoTemplate,
LayoutRepository repository) { @Override
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository); public Mono<Layout> createLayout(String pageId, Layout layout) {
if (pageId == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.PAGEID));
}
Mono<Page> pageMono = pageService
.findById(pageId)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.PAGEID)));
return pageMono
.map(page -> {
List<Layout> layoutList = page.getLayouts();
if (layoutList == null) {
//no layouts exist for this page
layoutList = new ArrayList<Layout>();
}
//Adding an Id to the layout to ensure that a layout can be referred to by its ID as well.
layout.setId(new ObjectId().toString());
layoutList.add(layout);
page.setLayouts(layoutList);
return page;
})
.flatMap(pageService::save)
.then(Mono.just(layout));
}
@Override
public Mono<Layout> getLayout(String pageId, String layoutId) {
return pageService.findByIdAndLayoutsId(pageId, layoutId)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.PAGEID + " or " + FieldName.LAYOUTID)))
.flatMap(pageService::doesPageIdBelongToCurrentUserOrganization)
//The pageId given is correct and belongs to the current user's organization.
.map(page -> {
List<Layout> layoutList = page.getLayouts();
//Because the findByIdAndLayoutsId call returned non-empty result, we are guaranteed to find the layoutId here.
return layoutList.stream().filter(layout -> layout.getId().equals(layoutId)).findFirst().get();
});
}
@Override
public Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout) {
return pageService.findByIdAndLayoutsId(pageId, layoutId)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.PAGEID + " or " + FieldName.LAYOUTID)))
.flatMap(pageService::doesPageIdBelongToCurrentUserOrganization)
//The pageId given is correct and belongs to the current user's organization.
.map(page -> {
List<Layout> layoutList = page.getLayouts();
//Because the findByIdAndLayoutsId call returned non-empty result, we are guaranteed to find the layoutId here.
for (Layout storedLayout : layoutList) {
if (storedLayout.getId().equals(layoutId)) {
BeanUtils.copyProperties(layout, storedLayout);
storedLayout.setId(layoutId);
break;
}
}
page.setLayouts(layoutList);
return page;
})
.flatMap(pageService::save)
.flatMap(page -> {
List<Layout> layoutList = page.getLayouts();
for (Layout storedLayout : layoutList) {
if (storedLayout.getId().equals(layoutId)) {
return Mono.just(storedLayout);
}
}
return Mono.empty();
});
} }
} }

View File

@ -56,7 +56,6 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORGANIZATION)); return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORGANIZATION));
} }
log.debug("Going to create the organization {}", organization.getName());
return Mono.just(organization) return Mono.just(organization)
.flatMap(this::validateObject) .flatMap(this::validateObject)
//transform the organization data to embed setting object in each object in organizationSetting list. //transform the organization data to embed setting object in each object in organizationSetting list.

View File

@ -0,0 +1,19 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Page;
import reactor.core.publisher.Mono;
public interface PageService extends CrudService<Page, String> {
Mono<Page> findById(String pageId);
Mono<Page> save(Page page);
Mono<Page> findByIdAndLayoutsId(String pageId, String layoutId);
Mono<Page> doesPageIdBelongToCurrentUserOrganization(Page page);
Mono<Page> findByName(String name);
Mono<Void> deleteAll();
}

View File

@ -0,0 +1,104 @@
package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Layout;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.PageRepository;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;
@Service
@Slf4j
public class PageServiceImpl extends BaseService<PageRepository, Page, String> implements PageService {
private final UserService userService;
private final ApplicationService applicationService;
@Autowired
public PageServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, PageRepository repository, UserService userService, ApplicationService applicationService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.userService = userService;
this.applicationService = applicationService;
}
@Override
public Mono<Page> create(Page page) {
if (page.getId() != null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
} else if (page.getName() == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.NAME));
} else if (page.getApplicationId() == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.APPLICATIONID));
}
List<Layout> layoutList = page.getLayouts();
if (layoutList == null) {
layoutList = new ArrayList<>();
}
if (layoutList.isEmpty()) {
layoutList.add(createDefaultLayout());
page.setLayouts(layoutList);
}
return repository.save(page);
}
@Override
public Mono<Page> findById(String pageId) {
return repository.findById(pageId);
}
@Override
public Mono<Page> save(Page page) {
return repository.save(page);
}
@Override
public Mono<Page> findByIdAndLayoutsId(String pageId, String layoutId) {
return repository.findByIdAndLayoutsId(pageId, layoutId);
}
@Override
public Mono<Page> doesPageIdBelongToCurrentUserOrganization(Page page) {
Mono<User> userMono = userService.getCurrentUser();
final String[] username = {null};
return userMono
.map(user -> {
username[0] = user.getEmail();
return user;
})
.flatMap(user -> applicationService.findByIdAndOrganizationId(page.getApplicationId(), user.getOrganizationId()))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PAGE_DOESNT_BELONG_TO_USER_ORGANIZATION, page.getId(), username[0])))
//If mono transmits, then application id belongs to the current user's organization. Return page.
.then(Mono.just(page));
}
@Override
public Mono<Page> findByName(String name) {
return repository.findByName(name);
}
private Layout createDefaultLayout() {
Layout layout = new Layout();
layout.setId(new ObjectId().toString());
return layout;
}
@Override
public Mono<Void> deleteAll() {
return repository.deleteAll();
}
}

View File

@ -0,0 +1,120 @@
package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ApplicationServiceTest {
@Autowired
ApplicationService applicationService;
@Test
@WithMockUser(username = "api_user")
public void createApplicationWithNullName() {
Application application = new Application();
Mono<Application> applicationMono = Mono.just(application)
.flatMap(applicationService::create);
StepVerifier
.create(applicationMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.NAME)))
.verify();
}
@Test
@WithMockUser(username = "api_user")
public void createValidApplication() {
Application testApplication = new Application();
testApplication.setName("ApplicationServiceTest TestApp");
Mono<Application> applicationMono = applicationService.create(testApplication);
StepVerifier
.create(applicationMono)
.assertNext(application -> {
assertThat(application).isNotNull();
assertThat(application.getId()).isNotNull();
assertThat(application.getName().equals("ApplicationServiceTest TestApp"));
})
.verifyComplete();
}
/* Tests for Get Application Flow */
@Test
public void getApplicationInvalidId() {
Mono<Application> applicationMono = applicationService.getById("random-id");
StepVerifier.create(applicationMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage("resource", "random-id")))
.verify();
}
@Test
public void getApplicationNullId() {
Mono<Application> applicationMono = applicationService.getById(null);
StepVerifier.create(applicationMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.ID)))
.verify();
}
@Test
@WithMockUser(username = "api_user")
public void validGetApplicationByName() {
Application application = new Application();
application.setName("validGetApplicationByName-Test");
Mono<Application> createApplication = applicationService.create(application);
Mono<Application> getApplication = createApplication.flatMap(t -> applicationService.getById(t.getId()));
StepVerifier.create(getApplication)
.assertNext(t -> {
assertThat(t).isNotNull();
assertThat(t.getId()).isNotNull();
assertThat(t.getName()).isEqualTo("validGetApplicationByName-Test");
})
.verifyComplete();
}
/* Tests for Update Application Flow */
@Test
@WithMockUser(username = "api_user")
public void validUpdateApplication() {
Application application = new Application();
application.setName("validUpdateApplication-Test");
Mono<Application> createApplication =
applicationService
.create(application);
Mono<Application> updateApplication = createApplication
.map(t -> {
t.setName("NewValidUpdateApplication-Test");
return t;
})
.flatMap(t -> applicationService.update(t.getId(), t))
.flatMap(t -> applicationService.getById(t.getId()));
StepVerifier.create(updateApplication)
.assertNext(t -> {
assertThat(t).isNotNull();
assertThat(t.getId()).isNotNull();
assertThat(t.getName()).isEqualTo("NewValidUpdateApplication-Test");
})
.verifyComplete();
}
}

View File

@ -0,0 +1,194 @@
package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Layout;
import com.appsmith.server.domains.Page;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.concurrent.atomic.AtomicReference;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class LayoutServiceTest {
@Autowired
LayoutService layoutService;
@Autowired
ApplicationService applicationService;
@Autowired
PageService pageService;
Mono<Layout> layoutMono;
Mono<Application> applicationMono;
@Before
@WithMockUser(username = "api_user")
public void setup() {
Application testApplication = new Application();
testApplication.setName("LayoutServiceTest TestApplications");
applicationMono =
applicationService.findByName(testApplication.getName())
.switchIfEmpty(applicationService.create(testApplication));
}
@Test
@WithMockUser(username = "api_user")
public void createLayoutWithNullPageId() {
Layout layout = new Layout();
Mono<Layout> layoutMono = layoutService.createLayout(null, layout);
StepVerifier
.create(layoutMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.PAGEID)))
.verify();
}
@Test
@WithMockUser(username = "api_user")
public void createLayoutWithInvalidPageID() {
Layout layout = new Layout();
String pageId = "Some random ID which can never be a page's ID";
Mono<Layout> layoutMono = layoutService.createLayout(pageId, layout);
StepVerifier
.create(layoutMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.PAGEID)))
.verify();
}
@Test
@WithMockUser(username = "api_user")
public void createValidLayout() {
Layout testLayout = new Layout();
JSONObject obj = new JSONObject();
obj.put("key1", "value1");
testLayout.setData(obj);
Page testPage = new Page();
testPage.setName("LayoutServiceTest createValidLayout Page");
Mono<Page> pageMono = pageService
.findByName(testPage.getName())
.switchIfEmpty(applicationMono
.map(application -> {
testPage.setApplicationId(application.getId());
return testPage;
})
.flatMap(pageService::save));
layoutMono = pageMono
.flatMap(page -> layoutService.createLayout(page.getId(), testLayout));
StepVerifier
.create(layoutMono)
.assertNext(layout -> {
assertThat(layout).isNotNull();
assertThat(layout.getId()).isNotNull();
assertThat(layout.getData().equals(obj));
})
.verifyComplete();
}
@Test
@WithMockUser(username = "api_user")
public void updateLayoutInvalidPageId() {
Layout testLayout = new Layout();
JSONObject obj = new JSONObject();
obj.put("key", "value");
testLayout.setData(obj);
AtomicReference<String> pageId = new AtomicReference<>();
Page testPage = new Page();
testPage.setName("LayoutServiceTest updateLayoutInvalidPage");
Mono<Page> pageMono = pageService
.findByName(testPage.getName())
.switchIfEmpty(applicationMono
.map(application -> {
testPage.setApplicationId(application.getId());
return testPage;
})
.flatMap(pageService::save));
Layout startLayout = pageMono
.flatMap(page -> {
pageId.set(page.getId());
return layoutService.createLayout(page.getId(), testLayout);
}).block();
Layout updateLayout = new Layout();
obj = new JSONObject();
obj.put("key", "value-updated");
updateLayout.setData(obj);
Mono<Layout> updatedLayoutMono = layoutService.updateLayout("random-impossible-id-page", startLayout.getId(), updateLayout);
StepVerifier
.create(updatedLayoutMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.PAGEID + " or " + FieldName.LAYOUTID)))
.verify();
}
@Test
@WithMockUser(username = "api_user")
public void updateLayoutValidPageId() {
Layout testLayout = new Layout();
JSONObject obj = new JSONObject();
obj.put("key", "value");
testLayout.setData(obj);
Page testPage = new Page();
testPage.setName("LayoutServiceTest updateLayoutValidPage");
Page page = pageService
.findByName(testPage.getName())
.switchIfEmpty(applicationMono
.map(application -> {
testPage.setApplicationId(application.getId());
return testPage;
})
.flatMap(pageService::save))
.block();
Layout startLayout = layoutService.createLayout(page.getId(), testLayout).block();
Layout updateLayout = new Layout();
JSONObject obj1 = new JSONObject();
obj1.put("key1", "value-updated");
updateLayout.setData(obj);
Mono<Layout> updatedLayoutMono = layoutService.updateLayout(page.getId(), startLayout.getId(), updateLayout);
StepVerifier
.create(updatedLayoutMono)
.assertNext(layout -> {
assertThat(layout).isNotNull();
assertThat(layout.getId()).isNotNull();
assertThat(layout.getData().equals(obj1));
})
.verifyComplete();
}
@After
public void purgePages() {
pageService.deleteAll();
}
}

View File

@ -0,0 +1,99 @@
package com.appsmith.server.services;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Page;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class PageServiceTest {
@Autowired
PageService pageService;
@Autowired
ApplicationService applicationService;
Mono<Application> applicationMono;
@Before
@WithMockUser(username = "api_user")
public void setup() {
purgeAllPages();
Application application = new Application();
application.setName("PageAPI-Test-Application");
applicationMono = applicationService.create(application);
}
@Test
@WithMockUser(username = "api_user")
public void createPageWithNullName() {
Page page = new Page();
Mono<Page> pageMono = Mono.just(page)
.flatMap(pageService::create);
StepVerifier
.create(pageMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.NAME)))
.verify();
}
@Test
@WithMockUser(username = "api_user")
public void createPageWithNullApplication() {
Page page = new Page();
page.setName("Page without application");
Mono<Page> pageMono = Mono.just(page)
.flatMap(pageService::create);
StepVerifier
.create(pageMono)
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.APPLICATIONID)))
.verify();
}
@Test
@WithMockUser(username = "api_user")
public void createValidPage() {
Page testPage = new Page();
testPage.setName("PageServiceTest TestApp");
Mono<Page> pageMono = applicationMono
.map(application -> {
testPage.setApplicationId(application.getOrganizationId());
return testPage;
})
.flatMap(pageService::create);
StepVerifier
.create(pageMono)
.assertNext(page -> {
assertThat(page).isNotNull();
assertThat(page.getId()).isNotNull();
assertThat("PageServiceTest TestApp".equals(page.getName()));
})
.verifyComplete();
}
@After
public void purgeAllPages() {
pageService.deleteAll();
}
}