Merge branch 'release' of https://github.com/appsmithorg/appsmith into release

This commit is contained in:
Automated Github Action 2020-08-19 10:05:37 +00:00
commit bc40d337f5
6 changed files with 162 additions and 16 deletions

View File

@ -13,6 +13,7 @@ import {
getAllUsers,
getCurrentOrg,
} from "selectors/organizationSelectors";
import Spinner from "components/editorComponents/Spinner";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { InviteUsersToOrgFormValues, inviteUsersToOrg } from "./helpers";
import { INVITE_USERS_TO_ORG_FORM } from "constants/forms";
@ -110,6 +111,12 @@ const StyledButton = styled(Button)`
}
`;
const Loading = styled(Spinner)`
padding-top: 10px;
margin: auto;
width: 100%;
`;
const validateFormValues = (values: { users: string; role: string }) => {
if (values.users && values.users.length > 0) {
const _users = values.users.split(",").filter(Boolean);
@ -158,6 +165,7 @@ const OrgInviteUsersForm = (props: any) => {
fetchCurrentOrg,
currentOrg,
isApplicationInvite,
isLoading,
} = props;
const currentPath = useLocation().pathname;
@ -237,16 +245,20 @@ const OrgInviteUsersForm = (props: any) => {
type="submit"
/>
</StyledInviteFieldGroup>
<UserList style={{ justifyContent: "space-between" }}>
{allUsers.map((user: { username: string; roleName: string }) => {
return (
<div className="user" key={user.username}>
<div>{user.username}</div>
<div>{user.roleName}</div>
</div>
);
})}
</UserList>
{isLoading ? (
<Loading size={30} />
) : (
<UserList style={{ justifyContent: "space-between" }}>
{allUsers.map((user: { username: string; roleName: string }) => {
return (
<div className="user" key={user.username}>
<div>{user.username}</div>
<div>{user.roleName}</div>
</div>
);
})}
</UserList>
)}
{!pathRegex.test(currentPath) && canManage && (
<Button
className="manageUsers"
@ -269,6 +281,7 @@ export default connect(
roles: getRolesForField(state),
allUsers: getAllUsers(state),
currentOrg: getCurrentOrg(state),
isLoading: state.ui.orgs.loadingStates.isFetchAllUsers,
};
},
(dispatch: any) => ({

View File

@ -84,4 +84,10 @@ public class PageController extends BaseController<PageService, Page, String> {
return service.delete(id)
.map(deletedResource -> new ResponseDTO<>(HttpStatus.OK.value(), deletedResource, null));
}
@PostMapping("/clone/{pageId}")
public Mono<ResponseDTO<Page>> clonePage(@PathVariable String pageId) {
return applicationPageService.clonePage(pageId)
.map(page -> new ResponseDTO<>(HttpStatus.CREATED.value(), page, null));
}
}

View File

@ -23,4 +23,6 @@ public interface ApplicationPageService {
Mono<Application> cloneApplication(Application application);
Mono<Application> deleteApplication(String id);
Mono<Page> clonePage(String pageId);
}

View File

@ -5,19 +5,23 @@ import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.acl.PolicyGenerator;
import com.appsmith.server.constants.AnalyticsEvents;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.ApplicationPage;
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.ApplicationPagesDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.ApplicationRepository;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
@ -26,6 +30,7 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
@ -40,26 +45,32 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
private final PageService pageService;
private final SessionUserService sessionUserService;
private final OrganizationService organizationService;
private final LayoutActionService layoutActionService;
private final AnalyticsService analyticsService;
private final PolicyGenerator policyGenerator;
private final ApplicationRepository applicationRepository;
private final ActionService actionService;
public ApplicationPageServiceImpl(ApplicationService applicationService,
PageService pageService,
SessionUserService sessionUserService,
OrganizationService organizationService,
LayoutActionService layoutActionService,
AnalyticsService analyticsService,
PolicyGenerator policyGenerator,
ApplicationRepository applicationRepository) {
ApplicationRepository applicationRepository,
ActionService actionService) {
this.applicationService = applicationService;
this.pageService = pageService;
this.sessionUserService = sessionUserService;
this.organizationService = organizationService;
this.layoutActionService = layoutActionService;
this.analyticsService = analyticsService;
this.policyGenerator = policyGenerator;
this.applicationRepository = applicationRepository;
this.actionService = actionService;
}
public Mono<Page> createPage(Page page) {
@ -324,4 +335,85 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
.flatMap(deletedObj -> analyticsService.sendEvent(AnalyticsEvents.DELETE + "_" + deletedObj.getClass().getSimpleName().toUpperCase(), (Application) deletedObj));
}
@Override
public Mono<Page> clonePage(String pageId) {
// Find the source page and then prune the page layout fields to only contain the required fields that should be
// copied.
Mono<Page> sourcePageMono = pageService.findById(pageId, MANAGE_PAGES)
.flatMap(page -> Flux.fromIterable(page.getLayouts())
.map(layout -> layout.getDsl())
.map(dsl -> {
Layout newLayout = new Layout();
String id = new ObjectId().toString();
newLayout.setId(id);
newLayout.setDsl(dsl);
return newLayout;
})
.collectList()
.map(layouts -> {
page.setLayouts(layouts);
return page;
}));
Flux<Action> sourceActionFlux = actionService.findByPageId(pageId, MANAGE_ACTIONS);
return sourcePageMono
.flatMap(page -> {
Mono<ApplicationPagesDTO> pageNamesMono = pageService
.findNamesByApplicationId(page.getApplicationId());
return pageNamesMono
// Set a unique name for the cloned page and then create the page.
.flatMap(pageNames -> {
Set<String> names = pageNames.getPages()
.stream()
.map(pageNameIdDTO -> pageNameIdDTO.getName()).collect(Collectors.toSet());
String newPageName = page.getName() + " Copy";
int i = 0;
String name = newPageName;
while(names.contains(name)) {
i++;
name = newPageName + i;
}
newPageName = name;
// Now we have a unique name. Proceed with creating the copy of the page
page.setId(null);
page.setName(newPageName);
return pageService.createDefault(page);
});
})
.flatMap(page -> {
String newPageId = page.getId();
return sourceActionFlux
.flatMap(action -> {
action.setId(null);
action.setPageId(newPageId);
return actionService.create(action);
})
.collectList()
.thenReturn(page);
})
// Calculate the onload actions for this page now that the page and actions have been created
.flatMap(savedPage -> {
List<Layout> layouts = savedPage.getLayouts();
return Flux.fromIterable(layouts)
.flatMap(layout -> layoutActionService.updateLayout(savedPage.getId(), layout.getId(), layout))
.collectList()
.thenReturn(savedPage);
})
.flatMap(page -> {
Mono<Application> applicationMono = applicationService.findById(page.getApplicationId(), MANAGE_APPLICATIONS);
return applicationMono
.flatMap(application -> {
ApplicationPage applicationPage = new ApplicationPage();
applicationPage.setId(page.getId());
application.getPages().add(applicationPage);
return applicationService.save(application)
.thenReturn(page);
});
});
}
}

View File

@ -46,7 +46,6 @@ public class LayoutActionServiceImpl implements LayoutActionService {
private final ActionService actionService;
private final PageService pageService;
private final ObjectMapper objectMapper;
private final ApplicationPageService applicationPageService;
private final AnalyticsService analyticsService;
/*
* This pattern finds all the String which have been extracted from the mustache dynamic bindings.
@ -67,12 +66,10 @@ public class LayoutActionServiceImpl implements LayoutActionService {
public LayoutActionServiceImpl(ActionService actionService,
PageService pageService,
ObjectMapper objectMapper,
ApplicationPageService applicationPageService,
AnalyticsService analyticsService) {
this.actionService = actionService;
this.pageService = pageService;
this.objectMapper = objectMapper;
this.applicationPageService = applicationPageService;
this.analyticsService = analyticsService;
}

View File

@ -1,7 +1,6 @@
package com.appsmith.server.services;
import com.appsmith.external.models.Policy;
import com.appsmith.server.configurations.WithMockAppsmithUser;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Page;
@ -17,7 +16,6 @@ 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.security.test.context.support.WithUserDetails;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
@ -174,6 +172,44 @@ public class PageServiceTest {
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void clonePage() throws ParseException {
Policy managePagePolicy = Policy.builder().permission(MANAGE_PAGES.getValue())
.users(Set.of("api_user"))
.build();
Policy readPagePolicy = Policy.builder().permission(READ_PAGES.getValue())
.users(Set.of("api_user"))
.build();
Page testPage = new Page();
testPage.setName("PageServiceTest CloneTest Source");
setupTestApplication();
testPage.setApplicationId(application.getId());
Mono<Page> pageMono = applicationPageService.createPage(testPage)
.flatMap(page -> applicationPageService.clonePage(page.getId()));
Object parsedJson = new JSONParser(JSONParser.MODE_PERMISSIVE).parse(FieldName.DEFAULT_PAGE_LAYOUT);
StepVerifier
.create(pageMono)
.assertNext(page -> {
assertThat(page).isNotNull();
assertThat(page.getId()).isNotNull();
assertThat("PageServiceTest CloneTest Source Copy".equals(page.getName()));
assertThat(page.getPolicies()).isNotEmpty();
assertThat(page.getPolicies()).containsOnly(managePagePolicy, readPagePolicy);
assertThat(page.getLayouts()).isNotEmpty();
assertThat(page.getLayouts().get(0).getDsl()).isEqualTo(parsedJson);
assertThat(page.getLayouts().get(0).getWidgetNames()).isNotEmpty();
assertThat(page.getLayouts().get(0).getPublishedDsl()).isNullOrEmpty();
})
.verifyComplete();
}
@After
public void purgeAllPages() {
pageService.deleteAll();