Skip to content

Commit

Permalink
DROOLS-7618 : Project Editor: Long description should not freeze the …
Browse files Browse the repository at this point in the history
…UI (#3825) (#3826)

Co-authored-by: Toni Rikkola <[email protected]>
  • Loading branch information
github-actions[bot] and Rikkola authored Jun 25, 2024
1 parent ce18edf commit cf53203
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.kie.workbench.common.screens.library.api.preferences;

import org.uberfire.preferences.shared.impl.validation.StringPropertyValidator;

public class DescriptionLengthValidator extends StringPropertyValidator {

private static final int DESCRIPTION_MAX_LENGTH = 3000;

public DescriptionLengthValidator() {
super(DescriptionLengthValidator::lengthCheck,
"PropertyValidator.ConstrainedValuesValidator.NotAllowed");
}

private static boolean lengthCheck(final String description) {
return description == null || description.length() <= DESCRIPTION_MAX_LENGTH;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public class LibraryProjectPreferences implements BasePreference<LibraryProjectP
String version;

@Property(bundleKey = "LibraryProjectPreferences.Description",
helpBundleKey = "LibraryProjectPreferences.Description.Help")
helpBundleKey = "LibraryProjectPreferences.Description.Help",
validators = {DescriptionLengthValidator.class})
String description;

@Property(bundleKey = "LibraryProjectPreferences.Branch",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.kie.workbench.common.screens.library.api.preferences;

import org.junit.Before;
import org.junit.Test;
import org.uberfire.preferences.shared.impl.validation.ValidationResult;

import static org.junit.Assert.*;

public class DescriptionLengthValidatorTest {

private DescriptionLengthValidator validator;

@Before
public void setUp() throws Exception {
validator = new DescriptionLengthValidator();
}

@Test
public void validDescriptionTest() {
final ValidationResult validationResult = validator.validate("test");
assertTrue(validationResult.isValid());
assertEquals(0,
validationResult.getMessagesBundleKeys().size());
}

@Test
public void invalidDescriptionTest() {

final StringBuilder builder = new StringBuilder();
for (int i = 0; i < 3001; i++) {
builder.append("x");
}

final ValidationResult validationResult = validator.validate(builder.toString());
assertFalse(validationResult.isValid());
assertEquals(1,
validationResult.getMessagesBundleKeys().size());
assertEquals("PropertyValidator.ConstrainedValuesValidator.NotAllowed",
validationResult.getMessagesBundleKeys().get(0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -793,4 +793,7 @@ public class LibraryConstants {

@TranslationKey(defaultValue = "")
public static final String InvalidProjectPath = "InvalidProjectPath";

@TranslationKey(defaultValue = "")
public static String DescriptionTooLong = "DescriptionTooLong";
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@

public class AddProjectPopUpPresenter {

private final static int DESCRIPTION_MAX_LENGTH = 3000;

public interface View extends UberElement<AddProjectPopUpPresenter>,
HasBusyIndicator {

Expand Down Expand Up @@ -133,6 +135,8 @@ void setTemplates(List<String> templates,
void enableBasedOnTemplatesCheckbox(boolean isEnabled);

void enableTemplatesSelect(boolean isEnabled);

String getDescriptionTooLongMessage();
}

private Caller<LibraryService> libraryService;
Expand Down Expand Up @@ -300,6 +304,7 @@ private void createProject(final DeploymentMode mode) {
groupId,
artifactId,
version,
description,
() -> {
Map<Class<? extends Throwable>, CommandWithThrowableDrivenErrorCallback.CommandWithThrowable> errors = new HashMap<Class<? extends Throwable>, CommandWithThrowableDrivenErrorCallback.CommandWithThrowable>() {{
put(GAVAlreadyExistsException.class, exception -> handleGAVAlreadyExistsException((GAVAlreadyExistsException) exception));
Expand Down Expand Up @@ -358,15 +363,23 @@ private void validateFields(final String name,
final String groupId,
final String artifactId,
final String version,
final String description,
final Command successCallback) {
final Command validateVersion = () -> validateVersion(version,
successCallback);
final Command validateArtifactId = () -> validateArtifactId(artifactId,
validateVersion);
final Command validateGroupId = () -> validateGroupId(groupId,
validateArtifactId);
validateName(name,
view.isAdvancedOptionsSelected() ? validateGroupId : successCallback);


if (!isDescriptionValid(description)) {
endProjectCreation();
view.showError(view.getDescriptionTooLongMessage());
} else {
validateName(name,
view.isAdvancedOptionsSelected() ? validateGroupId : successCallback);
}
}

private void validateName(final String name,
Expand All @@ -389,6 +402,10 @@ private void validateName(final String name,
}).isProjectNameValid(name);
}

private boolean isDescriptionValid(final String description) {
return description == null || description.length() <= DESCRIPTION_MAX_LENGTH;
}

private void validateGroupId(final String groupId,
final Command successCallback) {
if (groupId == null || groupId.trim().isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ public void enableTemplatesSelect(boolean isEnabled) {
enableBasedOnTemplate(isEnabled);
}

@Override
public String getDescriptionTooLongMessage() {
return ts.format(LibraryConstants.DescriptionTooLong);
}

private void modalSetup() {
this.modal = new CommonModalBuilder()
.addHeader(ts.format(LibraryConstants.AddProject))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@

public class GeneralSettingsPresenter extends Section<ProjectScreenModel> {

private static final int DESCRIPTION_MAX_LENGTH = 3000;

public interface View extends SectionView<GeneralSettingsPresenter> {

String getName();
Expand Down Expand Up @@ -93,6 +95,8 @@ public interface View extends SectionView<GeneralSettingsPresenter> {
String getInvalidVersionMessage();

String getDuplicatedProjectNameMessage();

String getDescriptionTooLongMessage();
}


Expand Down Expand Up @@ -179,7 +183,10 @@ public Promise<Object> validate() {

validateStringIsNotEmpty(pom.getGav().getVersion(), view.getEmptyVersionMessage())
.then(o -> executeValidation(s -> s.validateGAVVersion(pom.getGav().getVersion()), view.getInvalidVersionMessage()))
.catch_(this::showErrorAndReject)
.catch_(this::showErrorAndReject),


validateDescriptionLength().catch_(this::showErrorAndReject)
);
}

Expand All @@ -205,6 +212,16 @@ Promise<Boolean> validateStringIsNotEmpty(final String string,
});
}

private Promise<Object> validateDescriptionLength() {
return promises.create((resolve, reject) -> {
if(pom.getDescription() != null && pom.getDescription().length() > DESCRIPTION_MAX_LENGTH){
reject.onInvoke(view.getDescriptionTooLongMessage());
} else {
resolve.onInvoke(true);
}
});
}

<T> Promise<Boolean> executeValidation(final Caller<T> caller,
final Function<T, Boolean> call,
final String errorMessage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ public String getDuplicatedProjectNameMessage() {
return translationService.format(LibraryConstants.DuplicatedProjectName);
}

@Override
public String getDescriptionTooLongMessage() {
return translationService.format(LibraryConstants.DescriptionTooLong);
}

@Override
public String getTitle() {
return title.textContent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,3 +605,4 @@ ViewDeploymentDetails=View deployment details
Archetypes=Archetypes
ArchetypesDescription=Select all archetypes that will be available as templates for new projects, as well as a default one. This configuration is only applicable to this space.
InvalidProjectPath=No project found in the informed path.
DescriptionTooLong=Description is too long.
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,25 @@ public void newProjectIsCreated() throws Exception {
verify(view).setAddButtonEnabled(true);
}

@Test
public void newProjectIsCreatedDescriptionIsTooLong() throws Exception {
final OrganizationalUnit organizationalUnit = mock(OrganizationalUnit.class);
when(projectContext.getActiveOrganizationalUnit()).thenReturn(Optional.of(organizationalUnit));

doReturn("test").when(view).getName();
doReturn(get3001CharString()).when(view).getDescription();

presenter.add();


verify(libraryService, never()).createProject(
any(),
any(),
any(),
Mockito.<String> any()
);
}

@Test
public void newWorkbenchProjectWithAdvancedSettingsIsCreated() throws Exception {
final OrganizationalUnit organizationalUnit = mock(OrganizationalUnit.class);
Expand Down Expand Up @@ -259,7 +278,29 @@ public void newWorkbenchProjectWithAdvancedSettingsIsCreated() throws Exception
assertEquals("org.kie", pom.getBuild().getPlugins().get(0).getGroupId());
assertEquals("kie-maven-plugin", pom.getBuild().getPlugins().get(0).getArtifactId());
}


@Test
public void newWorkbenchProjectWithAdvancedSettingsTooLongDescription() throws Exception {
final OrganizationalUnit organizationalUnit = mock(OrganizationalUnit.class);
when(projectContext.getActiveOrganizationalUnit()).thenReturn(Optional.of(organizationalUnit));

doReturn("test").when(view).getName();
doReturn(get3001CharString()).when(view).getDescription();
doReturn("groupId").when(view).getGroupId();
doReturn("artifactId").when(view).getArtifactId();
doReturn("version").when(view).getVersion();
doReturn(true).when(view).isAdvancedOptionsSelected();

presenter.add();

verify(libraryService, never()).createProject(
any(),
any(),
any(),
Mockito.<String> any());

}

@Test
public void newWorkbenchProjectWithQuickSettingsIsCreated() throws Exception {
final OrganizationalUnit organizationalUnit = mock(OrganizationalUnit.class);
Expand Down Expand Up @@ -776,4 +817,12 @@ public void testLoadTemplatesWhenNoOneAvailable() {
verify(view).enableBasedOnTemplatesCheckbox(false);
verify(view).enableTemplatesSelect(false);
}

private static String get3001CharString() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 3001; i++) {
builder.append("x");
}
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,69 @@ public void testValidateWithOneError() {
never()).isProjectNameValid(any());
}

@Test
public void testValidateDescriptionDescriptionOk() {

generalSettingsPresenter.pom = new POM("",
null,
"",
new GAV());

doReturn("DescriptionTooLong").when(view).getDescriptionTooLongMessage();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 3000; i++) {
builder.append("x");
}
generalSettingsPresenter.setDescription(builder.toString());

generalSettingsPresenter.validate().then(i -> {
Assert.fail("Promise should've not been resolved!");
return promises.resolve();
});

verify(generalSettingsPresenter, never()).showErrorAndReject(eq("DescriptionTooLong"));
}
@Test
public void testValidateDescriptionDescriptionNull() {

generalSettingsPresenter.pom = new POM("",
null,
"",
new GAV());

doReturn("DescriptionTooLong").when(view).getDescriptionTooLongMessage();

generalSettingsPresenter.validate().then(i -> {
Assert.fail("Promise should've not been resolved!");
return promises.resolve();
});

verify(generalSettingsPresenter, never()).showErrorAndReject(eq("DescriptionTooLong"));
}

@Test
public void testValidateDescriptionDescriptionTooLong() {

generalSettingsPresenter.pom = new POM("",
null,
"",
new GAV());

doReturn("DescriptionTooLong").when(view).getDescriptionTooLongMessage();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 3001; i++) {
builder.append("x");
}
generalSettingsPresenter.setDescription(builder.toString());

generalSettingsPresenter.validate().then(i -> {
Assert.fail("Promise should've not been resolved!");
return promises.resolve();
});

verify(generalSettingsPresenter).showErrorAndReject(eq("DescriptionTooLong"));
}

@Test
public void testShowErrorAndRejectWithException() {
final RuntimeException testException = new RuntimeException("Test message");
Expand Down

0 comments on commit cf53203

Please sign in to comment.