Commit 587a96b9 authored by Adrien's avatar Adrien Committed by Romain Bioteau
Browse files

feat(app) set file name in creation wizard (#343)

Closes BS-16326
parent ddd2afcc
......@@ -9,7 +9,7 @@ version=Version
displayName=Display name
displayNameMessage=A display name used in the admin portal and in the default application layout.
required=Required
tokenValidatorMessage=Alphanumerics only
alphaNumericOnly=Alphanumerics only
source=Source
deployingLivingApplication=Deploying application descriptor...
deletingApplication=Deleting application descriptor...
......@@ -66,4 +66,7 @@ overwriteConfirmation=Overwrite ?
overwriteConfirmationMessage=An application descriptor with the same filename already exists.\nDo you want to overwrite it?
importLabel=Import
notAnApplicationError=Invalid application descriptor xml file
importApplicationDescriptorDesc=Select a valid application descriptor xml file to import.\nYou can export application descriptors from the Portal or from the Studio.
\ No newline at end of file
importApplicationDescriptorDesc=Select a valid application descriptor xml file to import.\nYou can export application descriptors from the Portal or from the Studio.
fileName=File name
invalidCharFileName='%s' is not allowed in filename
invalidFileName='%s' is not a valid filename
......@@ -80,8 +80,7 @@ public class NewApplicationHandlerTest {
final ApplicationNode applicationNode = newApplication("testAppToken", "My App Display Name", "0.1").create();
final Optional<ApplicationFileStore> fileStore = newApplicationHandler.createApplicationFileStore(
applicationNode,
repositoryAccessor);
applicationNode, repositoryAccessor, "testAppToken");
assertThat(fileStore).isPresent();
final ArgumentCaptor<ApplicationNodeContainer> captor = ArgumentCaptor.forClass(ApplicationNodeContainer.class);
......
package org.bonitasoft.studio.la.ui.validator;
import static org.bonitasoft.studio.assertions.StatusAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.bonitasoft.studio.la.repository.ApplicationFileStore;
import org.bonitasoft.studio.la.repository.ApplicationRepositoryStore;
import org.junit.Test;
public class ApplicationDescriptorFileNameValidatorTest {
@Test
public void should_validate_name_uniqueness() {
final ApplicationFileStore fileStore1 = mock(ApplicationFileStore.class);
when(fileStore1.getName()).thenReturn("fileName1.xml");
final ApplicationFileStore fileStore2 = mock(ApplicationFileStore.class);
when(fileStore2.getName()).thenReturn("fileName2.xml");
final List<ApplicationFileStore> children = new ArrayList<>();
children.add(fileStore1);
children.add(fileStore2);
final ApplicationRepositoryStore applicationRepositoryStore = mock(ApplicationRepositoryStore.class);
when(applicationRepositoryStore.getChildren()).thenReturn(children);
final ApplicationDescriptorFileNameValidator validator = new ApplicationDescriptorFileNameValidator(
applicationRepositoryStore);
assertThat(validator.validate("fileName1")).isNotOK();
assertThat(validator.validate("fileName2")).isNotOK();
assertThat(validator.validate("fileName3")).isOK();
}
@Test
public void should_reject_invalid_char() {
final ApplicationRepositoryStore applicationRepositoryStore = mock(ApplicationRepositoryStore.class);
when(applicationRepositoryStore.getChildren()).thenReturn(new ArrayList<ApplicationFileStore>());
final ApplicationDescriptorFileNameValidator validator = new ApplicationDescriptorFileNameValidator(
applicationRepositoryStore);
assertThat(validator.validate("fileName1/")).isNotOK();
assertThat(validator.validate("fileName2:")).isNotOK();
assertThat(validator.validate("fileName3")).isOK();
}
@Test
public void should_fail_if_no_filename() {
final ApplicationRepositoryStore applicationRepositoryStore = mock(ApplicationRepositoryStore.class);
when(applicationRepositoryStore.getChildren()).thenReturn(new ArrayList<ApplicationFileStore>());
final ApplicationDescriptorFileNameValidator validator = new ApplicationDescriptorFileNameValidator(
applicationRepositoryStore);
assertThat(validator.validate(null)).isNotOK();
}
}
package org.bonitasoft.studio.la.ui.validator;
import static org.bonitasoft.studio.assertions.StatusAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.bonitasoft.studio.common.jface.databinding.validator.UniqueValidator;
import org.bonitasoft.studio.la.repository.ApplicationFileStore;
import org.junit.Test;
public class ApplicationNameUnicityValidatorTest {
@Test
public void should_validate_uniqueness() {
ApplicationFileStore fileStore1 = mock(ApplicationFileStore.class);
when(fileStore1.getName()).thenReturn("fileName1.xml");
ApplicationFileStore fileStore2 = mock(ApplicationFileStore.class);
when(fileStore2.getName()).thenReturn("fileName2.xml");
List<ApplicationFileStore> children = new ArrayList<ApplicationFileStore>();
children.add(fileStore1);
children.add(fileStore2);
UniqueValidator validator = new ApplicationNameUnicityValidator().withApplicationDescriptors(children).create();
assertThat(validator.validate("fileName1")).isNotOK();
assertThat(validator.validate("fileName2")).isNotOK();
assertThat(validator.validate("fileName3")).isOK();
}
}
......@@ -31,6 +31,7 @@ import org.bonitasoft.studio.la.repository.ApplicationRepositoryStore;
import org.bonitasoft.studio.la.ui.control.NewApplicationPage;
import org.bonitasoft.studio.ui.wizard.WizardBuilder;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Shell;
public class NewApplicationHandler {
......@@ -38,6 +39,7 @@ public class NewApplicationHandler {
private static final String DEFAULT_PROFILE = "User";
private static final String DEFAULT_LAYOUT = "custompage_defaultlayout";
private static final String DEFAULT_THEME = "custompage_bootstrapdefaulttheme";
public static final String XML_EXTENSION = ".xml";
@Execute
public void openNewApplicationWizard(Shell activeShell, RepositoryAccessor repositoryAccessor) {
......@@ -53,21 +55,27 @@ public class NewApplicationHandler {
applicationNode.setLayout(DEFAULT_LAYOUT);
applicationNode.setTheme(DEFAULT_THEME);
final NewApplicationPage newApplicationPage = new NewApplicationPage(applicationNode, repositoryAccessor);
return builder
.withTitle(Messages.createNewApplicationDescriptor)
.withSize(SWT.DEFAULT, 480)
.havingPage(newPage()
.withTitle(Messages.newApplicationDescriptorTitle)
.withDescription(Messages.newApplicationDescription)
.withControl(new NewApplicationPage(applicationNode, repositoryAccessor)))
.onFinish(container -> createApplicationFileStore(applicationNode, repositoryAccessor));
.withControl(newApplicationPage))
.onFinish(container -> createApplicationFileStore(applicationNode, repositoryAccessor,
newApplicationPage.getFilename()));
}
protected Optional<ApplicationFileStore> createApplicationFileStore(ApplicationNode applicationNode,
RepositoryAccessor repositoryAccessor) {
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.xml", applicationNode.getToken())));
.createRepositoryFileStore(String.format("%s%s", fileNameTrimed, XML_EXTENSION)));
final ApplicationNodeContainer nodeContainer = newApplicationContainer().create();
nodeContainer.addApplication(applicationNode);
fileStore.ifPresent(file -> file.save(nodeContainer));
......
......@@ -30,7 +30,7 @@ public class Messages extends NLS {
public static String displayName;
public static String displayNameMessage;
public static String required;
public static String tokenValidatorMessage;
public static String alphaNumericOnly;
public static String source;
public static String deployingLivingApplication;
public static String deletingApplication;
......@@ -87,6 +87,9 @@ public class Messages extends NLS {
public static String importLabel;
public static String notAnApplicationError;
public static String importApplicationDescriptorDesc;
public static String fileName;
public static String invalidCharFileName;
public static String invalidFileName;
static {
NLS.initializeMessages("messages", Messages.class);
......
......@@ -20,7 +20,7 @@ import org.bonitasoft.engine.business.application.xml.ApplicationNode;
import org.bonitasoft.studio.common.repository.RepositoryAccessor;
import org.bonitasoft.studio.la.i18n.Messages;
import org.bonitasoft.studio.la.repository.ApplicationRepositoryStore;
import org.bonitasoft.studio.la.ui.validator.ApplicationNameUnicityValidator;
import org.bonitasoft.studio.la.ui.validator.ApplicationDescriptorFileNameValidator;
import org.bonitasoft.studio.la.ui.validator.ApplicationTokenUnicityValidator;
import org.bonitasoft.studio.ui.validator.EmptyInputValidator;
import org.bonitasoft.studio.ui.validator.MultiValidator;
......@@ -29,6 +29,7 @@ import org.bonitasoft.studio.ui.widget.TextWidget;
import org.bonitasoft.studio.ui.wizard.ControlSupplier;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.swt.SWT;
......@@ -37,8 +38,10 @@ import org.eclipse.swt.widgets.Control;
public class NewApplicationPage implements ControlSupplier {
public static final String DEFAULT_FILE_NAME = "applicationDescriptor";
private final ApplicationNode applicationNode;
private final RepositoryAccessor repositoryAccessor;
private String filename = DEFAULT_FILE_NAME;
public NewApplicationPage(ApplicationNode applicationNode, RepositoryAccessor repositoryAccessor) {
this.applicationNode = applicationNode;
......@@ -50,6 +53,20 @@ public class NewApplicationPage implements ControlSupplier {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(GridLayoutFactory.swtDefaults().create());
final IObservableValue filenameObservable = PojoObservables.observeValue(this, "filename");
new TextWidget.Builder()
.withLabel(Messages.fileName)
.labelAbove()
.fill()
.grabHorizontalSpace()
.bindTo(filenameObservable)
.inContext(ctx)
.withTargetToModelStrategy(updateValueStrategy()
.withValidator(new ApplicationDescriptorFileNameValidator(
repositoryAccessor.getRepositoryStore(ApplicationRepositoryStore.class))))
.createIn(composite);
new TextWidget.Builder()
.withLabel(Messages.applicationToken)
.withMessage(Messages.applicationTokenMessage)
......@@ -62,9 +79,7 @@ public class NewApplicationPage implements ControlSupplier {
.withValidator(new MultiValidator.Builder().havingValidators(
new EmptyInputValidator.Builder().withMessage(Messages.required),
new RegExpValidator.Builder().matches("^[a-zA-Z0-9]+$")
.withMessage(Messages.tokenValidatorMessage),
new ApplicationNameUnicityValidator().withApplicationDescriptors(repositoryAccessor
.getRepositoryStore(ApplicationRepositoryStore.class).getChildren()),
.withMessage(Messages.alphaNumericOnly),
new ApplicationTokenUnicityValidator.Builder(repositoryAccessor))))
.createIn(composite);
......@@ -94,4 +109,12 @@ public class NewApplicationPage implements ControlSupplier {
return composite;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
}
......@@ -223,14 +223,14 @@ public class ApplicationOverviewPage extends FormPage {
.withValidator(new MultiValidator.Builder().havingValidators(
new EmptyInputValidator.Builder().withMessage(Messages.required).create(),
new RegExpValidator.Builder().matches("^[a-zA-Z0-9]+$")
.withMessage(Messages.tokenValidatorMessage).create(),
.withMessage(Messages.alphaNumericOnly).create(),
applicationTokenUnicityValidator)))
.withModelToTargetStrategy(UpdateStrategyFactory.updateValueStrategy()
.withValidator(
new MultiValidator.Builder().havingValidators(
new EmptyInputValidator.Builder().withMessage(Messages.required).create(),
new RegExpValidator.Builder().matches("^[a-zA-Z0-9]+$")
.withMessage(Messages.tokenValidatorMessage).create(),
.withMessage(Messages.alphaNumericOnly).create(),
applicationTokenUnicityValidator)))
.inContext(ctx)
.adapt(toolkit)
......
......@@ -15,28 +15,63 @@
package org.bonitasoft.studio.la.ui.validator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.bonitasoft.studio.common.jface.databinding.validator.UniqueValidator;
import org.bonitasoft.studio.common.jface.databinding.validator.UniqueValidatorFactory;
import org.bonitasoft.studio.la.repository.ApplicationFileStore;
import org.bonitasoft.studio.ui.validator.ValidatorBuilder;
import org.bonitasoft.studio.la.handler.NewApplicationHandler;
import org.bonitasoft.studio.la.i18n.Messages;
import org.bonitasoft.studio.la.repository.ApplicationRepositoryStore;
import org.bonitasoft.studio.ui.validator.TypedValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
public class ApplicationNameUnicityValidator implements ValidatorBuilder<UniqueValidator> {
public class ApplicationDescriptorFileNameValidator extends TypedValidator<String, IStatus> {
private List<String> applicationDescriptorNames;
private static final String[] reservedChars = new String[] { "/", "\\", ":", "*", "?", "\"", "<", ">", ";", "|" };
public ApplicationNameUnicityValidator withApplicationDescriptors(List<ApplicationFileStore> applicationsDescriptors) {
applicationDescriptorNames = applicationsDescriptors.stream()
.map(applicationDescriptorName -> applicationDescriptorName.getName().substring(0,
applicationDescriptorName.getName().length() - ".xml".length()))
.collect(Collectors.toList());
return this;
private final ApplicationRepositoryStore store;
public ApplicationDescriptorFileNameValidator(ApplicationRepositoryStore store) {
this.store = store;
}
/*
* (non-Javadoc)
* @see org.bonitasoft.studio.ui.validator.TypedValidator#doValidate(java.util.Optional)
*/
@Override
public UniqueValidator create() {
return UniqueValidatorFactory.uniqueValidator().in(applicationDescriptorNames).create();
protected IStatus doValidate(Optional<String> value) {
final String fileName = value.orElse("");
if (fileName.isEmpty()) {
return ValidationStatus.error(Messages.required);
}
if (Objects.equals(fileName, NewApplicationHandler.XML_EXTENSION)) {
return ValidationStatus.error(String.format(Messages.invalidFileName, fileName));
}
final Optional<String> invalidChar = isValidFileName(fileName);
if (invalidChar.isPresent()) {
return ValidationStatus.error(String.format(Messages.invalidCharFileName, invalidChar.get()));
}
return UniqueValidatorFactory.uniqueValidator().in(getExistingFileNames()).create().validate(fileName);
}
protected Optional<String> isValidFileName(final String fileName) {
for (final String reservedChar : reservedChars) {
if (fileName.contains(reservedChar)) {
return Optional.of(reservedChar);
}
}
return Optional.empty();
}
private List<String> getExistingFileNames() {
return store.getChildren().stream()
.map(applicationDescriptor -> applicationDescriptor.getName().substring(0,
applicationDescriptor.getName().length() - NewApplicationHandler.XML_EXTENSION.length()))
.collect(Collectors.toList());
}
}
......@@ -15,24 +15,47 @@
package org.bonitasoft.studio.swtbot.framework.la;
import org.bonitasoft.studio.la.i18n.Messages;
import org.bonitasoft.studio.swtbot.framework.BotDialog;
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 BotDialog {
public class NewApplicationWizardBot extends BotWizardDialog {
public NewApplicationWizardBot(SWTGefBot bot, String dialogTitle) {
super(bot, dialogTitle);
}
public BotApplicationEditor createApplication(String token, String displayName) {
public NewApplicationWizardBot withFilename(String fileName) {
bot.textWithLabel(Messages.fileName).setText(fileName);
return this;
}
public NewApplicationWizardBot withToken(String token) {
bot.textWithLabel(Messages.applicationToken).setText(token);
return this;
}
public NewApplicationWizardBot withDisplayName(String displayName) {
bot.textWithLabel(Messages.displayName).setText(displayName);
return this;
}
public BotApplicationEditor create() {
finish();
return new BotApplicationEditor(bot, bot.activeEditor());
}
/*
* (non-Javadoc)
* @see org.bonitasoft.studio.swtbot.framework.BotWizardDialog#finish()
*/
@Override
public void finish() {
bot.waitUntil(Conditions.widgetIsEnabled(bot.button(Messages.create)));
SWTBotShell activeShell = bot.activeShell();
final SWTBotShell activeShell = bot.activeShell();
bot.button(Messages.create).click();
bot.waitUntil(Conditions.shellCloses(activeShell));
return new BotApplicationEditor(bot, bot.activeEditor());
}
}
......@@ -58,9 +58,9 @@ public class ApplicationDescriptorOverviewIT {
@Test
public void should_deploy_an_application_descriptor_from_overview() {
BotApplicationWorkbenchWindow workBenchBot = new BotApplicationWorkbenchWindow(bot);
final BotApplicationWorkbenchWindow workBenchBot = new BotApplicationWorkbenchWindow(bot);
createApplication(workBenchBot);
BotApplicationEditor botApplicationEditor = workBenchBot.openApplication().open("app1.xml ../apps/app1");
final BotApplicationEditor botApplicationEditor = workBenchBot.openApplication().open("app1.xml ../apps/app1");
botApplicationEditor.deploy();
workBenchBot.deleteApplicationDescriptor().delete("app1.xml ../apps/app1");
......@@ -68,9 +68,9 @@ public class ApplicationDescriptorOverviewIT {
@Test
public void should_delete_an_application_descriptor_from_overview() {
BotApplicationWorkbenchWindow workBenchBot = new BotApplicationWorkbenchWindow(bot);
final BotApplicationWorkbenchWindow workBenchBot = new BotApplicationWorkbenchWindow(bot);
createApplication(workBenchBot);
BotApplicationEditor botApplicationEditor = workBenchBot.openApplication().open("app1.xml ../apps/app1");
final BotApplicationEditor botApplicationEditor = workBenchBot.openApplication().open("app1.xml ../apps/app1");
botApplicationEditor.delete();
assertTrue(repositoryAccessor.getRepositoryStore(ApplicationRepositoryStore.class)
.getChildren().stream()
......@@ -78,7 +78,12 @@ public class ApplicationDescriptorOverviewIT {
}
private void createApplication(BotApplicationWorkbenchWindow workBenchBot) {
workBenchBot.newApplication().createApplication("app1", "My App").close();
workBenchBot.newApplication()
.withFilename("file1")
.withToken("app1")
.withDisplayName("My App")
.create()
.close();
}
}
......@@ -36,26 +36,37 @@ public class OpenExistingApplicationIT {
@Test
public void should_create_and_open_applications() {
BotApplicationWorkbenchWindow workBenchBot = new BotApplicationWorkbenchWindow(bot);
final BotApplicationWorkbenchWindow workBenchBot = new BotApplicationWorkbenchWindow(bot);
createApplications(workBenchBot);
BotApplicationEditor app1Editor = workBenchBot.openApplication().open("app1.xml ../apps/app1");
assertEquals("app1.xml", app1Editor.getTitle());
final BotApplicationEditor app1Editor = workBenchBot.openApplication().open("file1.xml ../apps/app1");
assertEquals("file1.xml", app1Editor.getTitle());
app1Editor.close();
workBenchBot.openApplication().open("app1.xml ../apps/app1", "app2.xml ../apps/app2");
workBenchBot.openApplication().open("file1.xml ../apps/app1", "file2.xml ../apps/app2");
assertEquals(2, bot.editors().size());
deleteApplications(workBenchBot);
}
private void deleteApplications(BotApplicationWorkbenchWindow workBenchBot) {
DeleteApplicationWizardBot deleteApplicationBot = workBenchBot.deleteApplicationDescriptor();
deleteApplicationBot.delete("app1.xml ../apps/app1", "app2.xml ../apps/app2");
final DeleteApplicationWizardBot deleteApplicationBot = workBenchBot.deleteApplicationDescriptor();
deleteApplicationBot.delete("file1.xml ../apps/app1", "file2.xml ../apps/app2");
}
private void createApplications(BotApplicationWorkbenchWindow workBenchBot) {
workBenchBot.newApplication().createApplication("app1", "My First App").close();
workBenchBot.newApplication().createApplication("app2", "My Second App").close();
workBenchBot.newApplication()
.withFilename("file1")
.withToken("app1")
.withDisplayName("My First App")
.create()
.close();
workBenchBot.newApplication()
.withFilename("file2")
.withToken("app2")
.withDisplayName("My Second App")
.create()
.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