diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java index 2b9b4ad15..0dbba50dc 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java @@ -20,6 +20,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor; import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil; import io.github.springwolf.core.asyncapi.scanners.common.utils.AsyncAnnotationUtil; +import io.github.springwolf.core.asyncapi.scanners.common.utils.TextUtils; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -67,6 +68,8 @@ protected Operation buildOperation(AsyncOperation asyncOperation, Method method, String description = this.resolver.resolveStringValue(asyncOperation.description()); if (!StringUtils.hasText(description)) { description = "Auto-generated description"; + } else { + description = TextUtils.trimIndent(description); } String operationTitle = channelName + "_" + this.asyncAnnotationProvider.getOperationType().type; diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java index 42d0a0917..5fc0cf7f8 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/AsyncAnnotationUtil.java @@ -115,6 +115,7 @@ public static void processAsyncMessageAnnotation( StringValueResolver resolver) { String annotationMessageDescription = resolver.resolveStringValue(asyncMessage.description()); if (StringUtils.hasText(annotationMessageDescription)) { + annotationMessageDescription = TextUtils.trimIndent(annotationMessageDescription); messageBuilder.description(annotationMessageDescription); } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/TextUtils.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/TextUtils.java new file mode 100644 index 000000000..5834c06ca --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/utils/TextUtils.java @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.common.utils; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * Auxiliary class to manage Description or long texts. + */ +public class TextUtils { + private TextUtils() {} + + /** + * This method removes the smallest common indentation from all the lines in the input string, + * and removes it. + * Any leading empty line will be also removed. + * + * @param text The original string with possible leading indentation. + * @return The string with leading indentation removed from each line. + */ + public static String trimIndent(String text) { + if (text == null) { + return null; + } + final String newLine = System.lineSeparator(); + String[] lines = text.split(newLine); + int firstNonEmptyLine = findFirstNonEmptyIndex(lines); + if (firstNonEmptyLine == -1) { + return text; + } + lines = Arrays.copyOfRange(lines, firstNonEmptyLine, lines.length); + int minIndent = resolveMinIndent(lines); + var result = Arrays.stream(lines) + .map(line -> line.substring(Math.min(line.length(), minIndent))) + .reduce((a, b) -> a + newLine + b) + .orElse(StringUtils.EMPTY); + + if (StringUtils.endsWith(text, "\n")) { + result = result.concat(newLine); + } + + return result; + } + + /** + * @return The index of the first non-empty line, or {@code -1} if all lines are empty + */ + private static int findFirstNonEmptyIndex(String[] lines) { + for (int i = 0; i < lines.length; i++) { + if (!lines[i].trim().isEmpty()) { + return i; + } + } + return -1; + } + + private static int resolveMinIndent(String[] lines) { + return Arrays.stream(lines) + .filter(line -> !line.trim().isEmpty()) + .mapToInt(line -> StringUtils.indexOfAnyBut(line, ' ')) + .filter(i -> i >= 0) + .min() + .orElse(0); + } +} diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/utils/TextUtilsTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/utils/TextUtilsTest.java new file mode 100644 index 000000000..491b2c8ca --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/utils/TextUtilsTest.java @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.common.utils; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TextUtilsTest { + + @Test + void textWithoutIndentShouldBeUntouched() { + var description = """ + This is a string + with two lines + """; + + var result = TextUtils.trimIndent(description); + + assertThat(result).isEqualTo("This is a string\nwith two lines\n"); + } + + @Test + void textWithSingleIndentShouldBeUntouched() { + var description = """ + This is a string + with two lines + """; + + var result = TextUtils.trimIndent(description); + + assertThat(result).isEqualTo(" This is a string\nwith two lines\n"); + } + + @Test + void removeIndent() { + var description = + """ + This is a string + with two lines + """; + + var result = TextUtils.trimIndent(description); + + assertThat(result).isEqualTo("This is a string\nwith two lines\n"); + } + + @Test + void simpleTextWithoutIndentShouldBeUntouched() { + var description = "This is a string\nwith two lines"; + var result = TextUtils.trimIndent(description); + + assertThat(result).isEqualTo("This is a string\nwith two lines"); + } + + @Test + void removeEmptyLinesUntilTextIsFound() { + var description = + """ + + This is a string + + with two lines + """; + var result = TextUtils.trimIndent(description); + + assertThat(result).isEqualTo("This is a string\n\nwith two lines\n"); + } + + @Test + void onlyEmptyLinesShouldBeUntouched() { + var description = "\n\n\n"; + var result = TextUtils.trimIndent(description); + + assertThat(result).isEqualTo("\n\n\n"); + } + + @Test + void nullTextShouldNotFail() { + String nullDescription = null; + var result = TextUtils.trimIndent(nullDescription); + assertThat(result).isNull(); + } +}