Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DROOLS-7618 : Project Editor: Long description should not freeze the UI #3825

Merged
merged 1 commit into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading