From 716c7c508326d15978c69487d8f914004fb9692f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 27 Aug 2020 08:11:23 +0200 Subject: [PATCH] Fix detection of markdown files for files having content does not start with '#' --- CHANGELOG.md | 1 + .../repos/sources/containers/SourcesView.tsx | 2 +- scm-webapp/pom.xml | 2 +- .../sonia/scm/api/v2/ContentTypeResolver.java | 51 +++++++++++++++ .../scm/api/v2/resources/ContentResource.java | 4 +- .../DiffResultToDiffResultDtoMapper.java | 4 +- .../scm/api/v2/ContentTypeResolverTest.java | 65 +++++++++++++++++++ 7 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/ContentTypeResolver.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/ContentTypeResolverTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 17064befab..c5dec347ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Fixed +- Fix detection of markdown files for files having content does not start with '#' ([#1306](https://github.com/scm-manager/scm-manager/pull/1306)) - Fix broken markdown rendering ([#1303](https://github.com/scm-manager/scm-manager/pull/1303)) - JWT token timeout is now handled properly ([#1297](https://github.com/scm-manager/scm-manager/pull/1297)) - Fix text-overflow in danger zone ([#1298](https://github.com/scm-manager/scm-manager/pull/1298)) diff --git a/scm-ui/ui-webapp/src/repos/sources/containers/SourcesView.tsx b/scm-ui/ui-webapp/src/repos/sources/containers/SourcesView.tsx index 417525c5f6..9cf3cfc3f3 100644 --- a/scm-ui/ui-webapp/src/repos/sources/containers/SourcesView.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/containers/SourcesView.tsx @@ -87,7 +87,7 @@ class SourcesView extends React.Component { const basePath = this.createBasePath(); if (contentType.startsWith("image/")) { return ; - } else if (contentType.includes("markdown")) { + } else if (contentType.includes("markdown") || (language && language.toLowerCase() === "markdown")) { return ; } else if (language) { return ; diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index b34eb7b5e8..e1db3ffc31 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -315,7 +315,7 @@ com.github.sdorra spotter-core - 2.1.2 + 3.0.0 diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/ContentTypeResolver.java b/scm-webapp/src/main/java/sonia/scm/api/v2/ContentTypeResolver.java new file mode 100644 index 0000000000..ff993689d0 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/ContentTypeResolver.java @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2; + +import com.github.sdorra.spotter.ContentType; +import com.github.sdorra.spotter.ContentTypeDetector; +import com.github.sdorra.spotter.Language; + +public final class ContentTypeResolver { + + private static final ContentTypeDetector PATH_BASED = ContentTypeDetector.builder() + .defaultPathBased().boost(Language.MARKDOWN) + .bestEffortMatch(); + + private static final ContentTypeDetector PATH_AND_CONTENT_BASED = ContentTypeDetector.builder() + .defaultPathAndContentBased().boost(Language.MARKDOWN) + .bestEffortMatch(); + + private ContentTypeResolver() { + } + + public static ContentType resolve(String path) { + return PATH_BASED.detect(path); + } + + public static ContentType resolve(String path, byte[] contentPrefix) { + return PATH_AND_CONTENT_BASED.detect(path, contentPrefix); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java index 74d1d2deeb..b58be79e51 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java @@ -25,7 +25,6 @@ package sonia.scm.api.v2.resources; import com.github.sdorra.spotter.ContentType; -import com.github.sdorra.spotter.ContentTypes; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; @@ -34,6 +33,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.NotFoundException; +import sonia.scm.api.v2.ContentTypeResolver; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -204,7 +204,7 @@ public class ContentResource { } private void appendContentHeader(String path, byte[] head, Response.ResponseBuilder responseBuilder) { - ContentType contentType = ContentTypes.detect(path, head); + ContentType contentType = ContentTypeResolver.resolve(path, head); responseBuilder.header("Content-Type", contentType.getRaw()); contentType.getLanguage().ifPresent( language -> responseBuilder.header(ProgrammingLanguages.HEADER, ProgrammingLanguages.getValue(language)) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffResultToDiffResultDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffResultToDiffResultDtoMapper.java index 0384ed7d45..955f11ae12 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffResultToDiffResultDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffResultToDiffResultDtoMapper.java @@ -24,10 +24,10 @@ package sonia.scm.api.v2.resources; -import com.github.sdorra.spotter.ContentTypes; import com.github.sdorra.spotter.Language; import com.google.inject.Inject; import de.otto.edison.hal.Links; +import sonia.scm.api.v2.ContentTypeResolver; import sonia.scm.repository.Repository; import sonia.scm.repository.api.DiffFile; import sonia.scm.repository.api.DiffLine; @@ -120,7 +120,7 @@ class DiffResultToDiffResultDtoMapper { dto.setOldRevision(file.getOldRevision()); - Optional language = ContentTypes.detect(path).getLanguage(); + Optional language = ContentTypeResolver.resolve(path).getLanguage(); language.ifPresent(value -> dto.setLanguage(ProgrammingLanguages.getValue(value))); List hunks = new ArrayList<>(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/ContentTypeResolverTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/ContentTypeResolverTest.java new file mode 100644 index 0000000000..d68770214c --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/ContentTypeResolverTest.java @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api.v2; + +import com.github.sdorra.spotter.ContentType; +import com.github.sdorra.spotter.Language; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; + +class ContentTypeResolverTest { + + @Test + void shouldResolveMarkdown() { + String content = String.join("\n", + "% Markdown content", + "% Which does not start with markdown" + ); + ContentType contentType = ContentTypeResolver.resolve("somedoc.md", content.getBytes(StandardCharsets.UTF_8)); + assertThat(contentType.getLanguage()).contains(Language.MARKDOWN); + } + + @Test + void shouldResolveMarkdownWithoutContent() { + ContentType contentType = ContentTypeResolver.resolve("somedoc.md"); + assertThat(contentType.getLanguage()).contains(Language.MARKDOWN); + } + + @Test + void shouldResolveMarkdownEvenWithDotsInFilename() { + ContentType contentType = ContentTypeResolver.resolve("somedoc.1.1.md"); + assertThat(contentType.getLanguage()).contains(Language.MARKDOWN); + } + + @Test + void shouldResolveDockerfile() { + ContentType contentType = ContentTypeResolver.resolve("Dockerfile"); + assertThat(contentType.getLanguage()).contains(Language.DOCKERFILE); + } + +}