Commit 8efe3731 authored by Adrien's avatar Adrien Committed by Romain Bioteau
Browse files

feat(app) new application dialog (#378)

* Application file name is now generated
* An information wizard pop up when an user creates an application, with
a "don't show it again" option. 
* default token is incremented to avoid validation error.

closes[BS-16397](https://bonitasoft.atlassian.net/browse/BS-16397)


parent ad68d951
......@@ -26,7 +26,9 @@ Import-Package: org.bonitasoft.engine.business.application,
org.bonitasoft.engine.business.application.exporter,
org.bonitasoft.engine.business.application.xml
Bundle-ActivationPolicy: lazy
Export-Package: org.bonitasoft.studio.la.core,
Export-Package: org.bonitasoft.studio.la,
org.bonitasoft.studio.la.core,
org.bonitasoft.studio.la.handler,
org.bonitasoft.studio.la.i18n,
org.bonitasoft.studio.la.repository,
org.bonitasoft.studio.la.ui.validator
applicationStoreName=Applications
create=Create
newApplicationDescriptorTitle=Create a new Application Descriptor Container
newApplicationDescriptorTitle=New Application File
newApplicationDescription=Choose a filename to create a new application descriptor container
applicationTokenMessage=A unique identifier used as url endpoint for the application (../apps/<url>)
applicationToken=Application Token
......@@ -45,4 +45,7 @@ exportOperation=Export operation
exporting=Exporting %s...
deleteDescriptor='%s' application descriptor will be removed from this file and deleted from the portal.\nConfirm deletion ?
deleteDescriptorDone='%s' has been deleted
deleteContainer=Delete the file %s, with all its application descriptors
\ No newline at end of file
deleteContainer=Delete the file %s, with all its application descriptors
applicationInfo=An application file contains one or several application descriptors.\nSince each application is dedicated to a unique user profile, you can use this file to bundle together applications on the same topic.\n\nExample:\nManaging leave requests with Bonita BPM can be handled with such a file, containing one application descriptor for employees,\none for managers, one for HR officers, and one for administrators
applicationDetails=Application descriptors hold meta data about the application (url token, name, version, user profile), but also its navigation,\nas well as links to the pages it needs to access.\nThey were created in the Portal prior to Bonita BPM 7.5 and are now handled in the Studio to ease packaging and deployment.\nThe Studio is not aware of what is created in the Portal, so deploying an application descriptor may overwrite some Portal content.
doNotShowMeAgain=Do not show me this dialog again
......@@ -15,67 +15,31 @@
package org.bonitasoft.studio.la.handler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.notNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Optional;
import org.bonitasoft.studio.common.repository.RepositoryAccessor;
import org.bonitasoft.studio.la.i18n.Messages;
import org.bonitasoft.studio.la.repository.ApplicationFileStore;
import org.bonitasoft.studio.la.repository.ApplicationRepositoryStore;
import org.bonitasoft.studio.ui.wizard.FinishHandler;
import org.bonitasoft.studio.ui.wizard.WizardBuilder;
import org.bonitasoft.studio.ui.wizard.WizardPageBuilder;
import org.eclipse.swt.widgets.Shell;
import org.junit.Test;
public class NewApplicationHandlerTest {
@Test
public void should_open_a_new_application_wizard() throws Exception {
final NewApplicationHandler newApplicationHandler = spy(new NewApplicationHandler());
final RepositoryAccessor repositoryAccessor = mock(RepositoryAccessor.class);
final ApplicationFileStore applicationFileStore = mock(ApplicationFileStore.class);
final WizardBuilder<ApplicationFileStore> wizardBuilder = mock(WizardBuilder.class);
doReturn(wizardBuilder).when(newApplicationHandler).createWizard(notNull(WizardBuilder.class),
any(RepositoryAccessor.class));
when(wizardBuilder.open(any(Shell.class), eq(Messages.create))).thenReturn(Optional.of(applicationFileStore));
newApplicationHandler.openNewApplicationWizard(mock(Shell.class), repositoryAccessor);
verify(wizardBuilder).open(notNull(Shell.class), eq(Messages.create));
}
@Test
public void should_create_a_new_application_wizard() throws Exception {
final NewApplicationHandler newApplicationHandler = spy(new NewApplicationHandler());
final RepositoryAccessor repositoryAccessor = mock(RepositoryAccessor.class);
final WizardBuilder<ApplicationFileStore> builder = spy(WizardBuilder.newWizard());
newApplicationHandler.createWizard(builder, repositoryAccessor);
verify(builder).withTitle(Messages.newApplicationDescriptorTitle);
verify(builder).havingPage(notNull(WizardPageBuilder.class));
verify(builder).onFinish(notNull(FinishHandler.class));
}
@Test
public void should_create_an_applicationFileStore_onFinish() throws Exception {
public void should_create_an_applicationFileStore() throws Exception {
final NewApplicationHandler newApplicationHandler = new NewApplicationHandler();
final RepositoryAccessor repositoryAccessor = mock(RepositoryAccessor.class);
final ApplicationRepositoryStore applicationStore = mock(ApplicationRepositoryStore.class);
final ApplicationFileStore applicationFileStore = mock(ApplicationFileStore.class);
when(applicationStore.createRepositoryFileStore("testAppToken.xml")).thenReturn(applicationFileStore);
when(applicationStore.createRepositoryFileStore(NewApplicationHandler.DEFAULT_FILE_NAME + ".xml"))
.thenReturn(applicationFileStore);
when(repositoryAccessor.getRepositoryStore(ApplicationRepositoryStore.class)).thenReturn(applicationStore);
final Optional<ApplicationFileStore> fileStore = newApplicationHandler.createApplicationFileStore(repositoryAccessor,
"testAppToken");
final Optional<ApplicationFileStore> fileStore = Optional
.ofNullable(newApplicationHandler.createApplicationFileStore(repositoryAccessor,
NewApplicationHandler.DEFAULT_FILE_NAME + ".xml"));
assertThat(fileStore).isPresent();
}
......
......@@ -15,59 +15,63 @@
package org.bonitasoft.studio.la.handler;
import static org.bonitasoft.engine.business.application.xml.ApplicationNodeBuilder.newApplicationContainer;
import static org.bonitasoft.studio.ui.wizard.WizardBuilder.newWizard;
import static org.bonitasoft.studio.ui.wizard.WizardPageBuilder.newPage;
import java.util.Optional;
import java.util.List;
import java.util.stream.Collectors;
import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer;
import org.bonitasoft.studio.common.jface.MessageDialogWithPrompt;
import org.bonitasoft.studio.common.repository.RepositoryAccessor;
import org.bonitasoft.studio.common.repository.model.IRepositoryFileStore;
import org.bonitasoft.studio.la.LivingApplicationPlugin;
import org.bonitasoft.studio.la.i18n.Messages;
import org.bonitasoft.studio.la.repository.ApplicationFileStore;
import org.bonitasoft.studio.la.repository.ApplicationRepositoryStore;
import org.bonitasoft.studio.la.ui.control.NewApplicationPage;
import org.bonitasoft.studio.ui.wizard.WizardBuilder;
import org.bonitasoft.studio.ui.util.StringIncrementer;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Shell;
public class NewApplicationHandler {
public static final String XML_EXTENSION = ".xml";
public static final String DEFAULT_FILE_NAME = "applicationDescriptorFile";
public static final String DO_NOT_SHOW_HELP_MESSAGE_DIALOG = "DO_NOT_SHOW_HELP_MESSAGE_DIALOG";
@Execute
public void openNewApplicationWizard(Shell activeShell, RepositoryAccessor repositoryAccessor) {
createWizard(newWizard(), repositoryAccessor)
.open(activeShell, Messages.create)
.ifPresent(IRepositoryFileStore::open);
}
protected WizardBuilder<ApplicationFileStore> createWizard(WizardBuilder<ApplicationFileStore> builder,
RepositoryAccessor repositoryAccessor) {
final NewApplicationPage newApplicationPage = new NewApplicationPage(repositoryAccessor);
return builder
.withTitle(Messages.newApplicationDescriptorTitle)
.withSize(SWT.DEFAULT, 235)
.havingPage(newPage()
.withTitle(Messages.newApplicationDescriptorTitle)
.withDescription(Messages.newApplicationDescription)
.withControl(newApplicationPage))
.onFinish(container -> createApplicationFileStore(repositoryAccessor, newApplicationPage.getFilename()));
final IPreferenceStore preferenceStore = LivingApplicationPlugin.getDefault().getPreferenceStore();
if (!preferenceStore.getBoolean(DO_NOT_SHOW_HELP_MESSAGE_DIALOG)) {
MessageDialogWithPrompt.openWithDetails(MessageDialog.INFORMATION,
activeShell,
Messages.newApplicationDescriptorTitle,
Messages.applicationInfo,
Messages.doNotShowMeAgain,
Messages.applicationDetails,
false,
preferenceStore,
DO_NOT_SHOW_HELP_MESSAGE_DIALOG,
SWT.NONE);
}
List<String> existingFileNameList = repositoryAccessor.getRepositoryStore(ApplicationRepositoryStore.class)
.getChildren().stream().map(ApplicationFileStore::getDisplayName).collect(Collectors.toList());
createApplicationFileStore(repositoryAccessor,
StringIncrementer.getIncrementedString(DEFAULT_FILE_NAME, existingFileNameList)).open();
}
protected Optional<ApplicationFileStore> createApplicationFileStore(RepositoryAccessor repositoryAccessor,
protected ApplicationFileStore createApplicationFileStore(RepositoryAccessor repositoryAccessor,
String fileName) {
final ApplicationRepositoryStore repositoryStore = repositoryAccessor
.getRepositoryStore(ApplicationRepositoryStore.class);
final String fileNameTrimed = fileName.endsWith(XML_EXTENSION)
? fileName.substring(0, fileName.length() - XML_EXTENSION.length()) : fileName;
final Optional<ApplicationFileStore> fileStore = Optional.ofNullable(repositoryStore
.createRepositoryFileStore(String.format("%s%s", fileNameTrimed, XML_EXTENSION)));
final ApplicationFileStore fileStore = repositoryStore
.createRepositoryFileStore(String.format("%s%s", fileNameTrimed, XML_EXTENSION));
final ApplicationNodeContainer nodeContainer = newApplicationContainer().create();
fileStore.ifPresent(file -> file.save(nodeContainer));
fileStore.save(nodeContainer);
return fileStore;
}
......
......@@ -66,6 +66,9 @@ public class Messages extends NLS {
public static String deleteDescriptor;
public static String deleteDescriptorDone;
public static String deleteContainer;
public static String applicationInfo;
public static String doNotShowMeAgain;
public static String applicationDetails;
static {
NLS.initializeMessages("messages", Messages.class);
......
......@@ -57,7 +57,7 @@ public class ApplicationTokenUnicityValidator extends UniqueValidator {
this.currentToken = Optional.ofNullable(currentToken);
}
private List<String> getTokenList() {
public List<String> getTokenList() {
final List<String> allTokens = repositoryAccessor.getRepositoryStore(ApplicationRepositoryStore.class).getChildren()
.stream()
.map(fStore -> {
......
package org.bonitasoft.studio.ui.util;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class StringIncrementerTest {
private String defaultFileName = "defaultFileName";
@Test
public void should_increment_file_name() {
List<String> existingFileNameList = new ArrayList<>();
existingFileNameList.add(defaultFileName + "a");
String filename = StringIncrementer.getIncrementedString(defaultFileName, existingFileNameList);
assertThat(filename).isEqualTo(defaultFileName);
existingFileNameList.add(filename);
filename = StringIncrementer.getIncrementedString(defaultFileName, existingFileNameList);
assertThat(filename).isEqualTo(defaultFileName + "1");
}
}
/*******************************************************************************
* Copyright (C) 2017 BonitaSoft S.A.
* BonitaSoft is a trademark of BonitaSoft SA.
* This software file is BONITASOFT CONFIDENTIAL. Not For Distribution.
* For commercial licensing information, contact:
* BonitaSoft, 32 rue Gustave Eiffel – 38000 Grenoble
* or BonitaSoft US, 51 Federal Street, Suite 305, San Francisco, CA 94107
*******************************************************************************/
package org.bonitasoft.studio.ui.util;
import java.util.List;
public class StringIncrementer {
public static String getIncrementedString(String defaultString, List<String> existingStringList) {
int id = existingStringList.stream()
.filter(fileName -> fileName.startsWith(defaultString))
.map(fileName -> getEndString(fileName, defaultString))
.filter(StringIncrementer::isInt)
.mapToInt(Integer::parseInt)
.reduce(-1, Integer::max);
return id < 0 ? defaultString : defaultString + (id + 1);
}
private static String getEndString(String string, String defaultString) {
String subString = string.substring(defaultString.length());
return subString.isEmpty() ? "0" : subString;
}
private static boolean isInt(String s) {
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
......@@ -28,18 +28,19 @@ import org.bonitasoft.studio.swtbot.framework.diagram.export.BotExportBOSDialog;
import org.bonitasoft.studio.swtbot.framework.diagram.importer.BotImportBOSDialog;
import org.bonitasoft.studio.swtbot.framework.diagram.importer.BotImportOtherDialog;
import org.bonitasoft.studio.swtbot.framework.la.DeleteApplicationWizardBot;
import org.bonitasoft.studio.swtbot.framework.la.NewApplicationWizardBot;
import org.bonitasoft.studio.swtbot.framework.la.OpenApplicationWizardBot;
import org.bonitasoft.studio.test.swtbot.util.SWTBotTestUtil;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor;
import org.eclipse.swtbot.eclipse.finder.waits.Conditions;
import org.eclipse.swtbot.eclipse.gef.finder.SWTGefBot;
import org.eclipse.swtbot.eclipse.gef.finder.matchers.IsInstanceOf;
import org.eclipse.swtbot.swt.finder.SWTBot;
import org.eclipse.swtbot.swt.finder.matchers.WithId;
import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
import org.eclipse.swtbot.swt.finder.waits.ICondition;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu;
import org.eclipse.ui.IEditorReference;
/**
* Application workbench.
......@@ -171,9 +172,9 @@ public class BotApplicationWorkbenchWindow extends AbstractBotMenu {
return new OpenApplicationWizardBot(bot, Messages.openExistingApplication);
}
public NewApplicationWizardBot newApplicationContainer() {
public void newApplicationContainer() {
bot.menu("Development").menu("Application Descriptor").menu("New...").click();
return new NewApplicationWizardBot(bot, Messages.newApplicationDescriptorTitle);
bot.waitUntil(Conditions.waitForEditor(IsInstanceOf.instanceOf(IEditorReference.class)));
}
public DeleteApplicationWizardBot deleteApplicationDescriptor() {
......
/**
* Copyright (C) 2017 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.swtbot.framework.la;
import org.bonitasoft.studio.la.i18n.Messages;
import org.bonitasoft.studio.swtbot.framework.BotWizardDialog;
import org.eclipse.swtbot.eclipse.gef.finder.SWTGefBot;
import org.eclipse.swtbot.swt.finder.waits.Conditions;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
public class NewApplicationWizardBot extends BotWizardDialog {
public NewApplicationWizardBot(SWTGefBot bot, String dialogTitle) {
super(bot, dialogTitle);
}
public NewApplicationWizardBot withFilename(String fileName) {
bot.textWithLabel(Messages.fileName).setText(fileName);
return this;
}
@Override
public void finish() {
bot.waitUntil(Conditions.widgetIsEnabled(bot.button(Messages.create)));
final SWTBotShell activeShell = bot.activeShell();
bot.button(Messages.create).click();
bot.waitUntil(Conditions.shellCloses(activeShell));
}
}
......@@ -16,12 +16,16 @@ package org.bonitasoft.studio.tests.applicationDescriptor;
import static org.junit.Assert.assertEquals;
import org.bonitasoft.studio.la.LivingApplicationPlugin;
import org.bonitasoft.studio.la.handler.NewApplicationHandler;
import org.bonitasoft.studio.swtbot.framework.application.BotApplicationWorkbenchWindow;
import org.bonitasoft.studio.swtbot.framework.la.DeleteApplicationWizardBot;
import org.bonitasoft.studio.swtbot.framework.rule.SWTGefBotRule;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;
import org.eclipse.swtbot.eclipse.gef.finder.SWTGefBot;
import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -30,22 +34,37 @@ import org.junit.runner.RunWith;
public class OpenExistingApplicationIT {
private final SWTGefBot bot = new SWTGefBot();
private boolean initPref;
@Rule
public SWTGefBotRule botRule = new SWTGefBotRule(bot);
@Before
public void init() {
initPref = LivingApplicationPlugin.getDefault().getPreferenceStore()
.getBoolean(NewApplicationHandler.DO_NOT_SHOW_HELP_MESSAGE_DIALOG);
LivingApplicationPlugin.getDefault().getPreferenceStore()
.setValue(NewApplicationHandler.DO_NOT_SHOW_HELP_MESSAGE_DIALOG, true);
}
@After
public void resetPreference() {
LivingApplicationPlugin.getDefault().getPreferenceStore()
.setValue(NewApplicationHandler.DO_NOT_SHOW_HELP_MESSAGE_DIALOG, initPref);
}
@Test
public void should_create_and_open_applications() {
final BotApplicationWorkbenchWindow workBenchBot = new BotApplicationWorkbenchWindow(bot);
createApplications(workBenchBot);
workBenchBot.openApplication().select("file1.xml").finish();
workBenchBot.openApplication().select(NewApplicationHandler.DEFAULT_FILE_NAME + ".xml").finish();
final SWTBotEditor app1Editor = bot.activeEditor();
assertEquals("file1.xml", app1Editor.getTitle());
assertEquals(NewApplicationHandler.DEFAULT_FILE_NAME + ".xml", app1Editor.getTitle());
app1Editor.close();
workBenchBot.openApplication()
.select("file1.xml", "file2.xml")
.select(NewApplicationHandler.DEFAULT_FILE_NAME + ".xml", NewApplicationHandler.DEFAULT_FILE_NAME + "1.xml")
.finish();
assertEquals(2, bot.editors().size());
......@@ -54,19 +73,16 @@ public class OpenExistingApplicationIT {
private void deleteApplications(BotApplicationWorkbenchWindow workBenchBot) {
final DeleteApplicationWizardBot deleteApplicationBot = workBenchBot.deleteApplicationDescriptor();
deleteApplicationBot.select("file1.xml", "file2.xml")
deleteApplicationBot
.select(NewApplicationHandler.DEFAULT_FILE_NAME + ".xml", NewApplicationHandler.DEFAULT_FILE_NAME + "1.xml")
.delete();
}
private void createApplications(BotApplicationWorkbenchWindow workBenchBot) {
workBenchBot.newApplicationContainer()
.withFilename("file1")
.finish();
workBenchBot.newApplicationContainer();
bot.activeEditor().close();
workBenchBot.newApplicationContainer()
.withFilename("file2")
.finish();
workBenchBot.newApplicationContainer();
bot.activeEditor().close();
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment