diff --git a/src/main/java/org/jlab/sim/business/service/SyncService.java b/src/main/java/org/jlab/sim/business/service/SyncService.java index 6eff2db..84a9894 100644 --- a/src/main/java/org/jlab/sim/business/service/SyncService.java +++ b/src/main/java/org/jlab/sim/business/service/SyncService.java @@ -150,7 +150,7 @@ private static List parseProjects( String description = null; if (!item.isNull(DESCRIPTION)) { - description = item.getString(DESCRIPTION); + description = normalizeLineEndings(item.getString(DESCRIPTION)); } JsonArray topics = item.getJsonArray("topics"); @@ -180,6 +180,19 @@ private static List parseProjects( return softwareList; } + /** + * Replaces CRLF and CR line endings with LF. GitLab returns a description entered in its web form + * with CRLF line endings, but the sync page passes it through an HTML attribute, which the + * browser reads with LF line endings, so sim saves LF. Without this a synced description with a + * line break never matches. + * + * @param text The text + * @return The text with LF line endings + */ + static String normalizeLineEndings(String text) { + return text.replace("\r\n", "\n").replace('\r', '\n'); + } + private static final Pattern LINK_PATTERN = Pattern.compile("<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\""); /** The next page's URL from a response's first Link header, or null on the last page. */ diff --git a/src/test/java/org/jlab/sim/business/service/SyncServiceParseTest.java b/src/test/java/org/jlab/sim/business/service/SyncServiceParseTest.java index b30cfb7..a26d507 100644 --- a/src/test/java/org/jlab/sim/business/service/SyncServiceParseTest.java +++ b/src/test/java/org/jlab/sim/business/service/SyncServiceParseTest.java @@ -12,6 +12,7 @@ import org.jlab.sim.persistence.entity.Repository; import org.jlab.sim.persistence.entity.Software; import org.jlab.sim.persistence.enumeration.SoftwareType; +import org.jlab.sim.persistence.view.SoftwareDiff; import org.jlab.smoothness.business.exception.UserFriendlyException; import org.jsoup.Jsoup; import org.junit.jupiter.api.Test; @@ -89,6 +90,40 @@ void gitLabProjectsBecomeSoftware() throws Exception { assertTrue(notes.isArchived()); } + @Test + void multiLineDescriptionMatchesWhatSimSaved() throws Exception { + // GitLab returns a description entered in its web form with CRLF line endings; the sync page + // passes it through an HTML attribute, which the browser reads with LF line endings (sim#31) + String json = + """ + [ + {"name": "fsdlib", "web_url": "https://code.jlab.org/acc/ops/fsdlib", + "description": "FSD library\\r\\nfor apps\\rand scripts\\n", "topics": [], + "archived": false} + ] + """; + + List remoteList = SyncService.parseGitLabProjects(json, REPOSITORY); + + assertEquals("FSD library\nfor apps\nand scripts\n", remoteList.get(0).getDescription()); + + Software saved = + new Software( + REPOSITORY, + "fsdlib", + SoftwareType.APP, + "FSD library\nfor apps\nand scripts\n", + null, + "https://code.jlab.org/acc/ops/fsdlib", + false, + null); + + SoftwareDiff diff = new SyncService().diff(List.of(saved), Map.of("fsdlib", remoteList.get(0))); + + assertEquals(1, diff.getMatchList().size(), "matched, not updated again"); + assertFalse(diff.hasChanges()); + } + @Test void emptyResultsAreNoSoftware() throws Exception { assertEquals(