mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-18 02:00:22 +01:00
Add api to overwrite content type resolver (#2051)
Introduce content type resolver extension to provide custom content types for specific file extensions.
This commit is contained in:
2
gradle/changelog/contentTypeResolverExtension.yaml
Normal file
2
gradle/changelog/contentTypeResolverExtension.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: added
|
||||
description: Add api to overwrite content type resolver ([#2051](https://github.com/scm-manager/scm-manager/pull/2051))
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.io;
|
||||
|
||||
import sonia.scm.plugin.ExtensionPoint;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* ContentTypeResolverExtension extends the default {@link ContentTypeResolver} with custom resolve actions.
|
||||
* This can be used by plugins which want to change the content type of specific file extensions.
|
||||
*
|
||||
* @since 2.36.0
|
||||
*/
|
||||
@ExtensionPoint
|
||||
public interface ContentTypeResolverExtension {
|
||||
|
||||
Optional<String> resolve(String path, byte[] contentPrefix);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ const SourcesView: FC<Props> = ({ file, repository, revision }) => {
|
||||
file,
|
||||
contentType,
|
||||
revision,
|
||||
basePath
|
||||
basePath,
|
||||
}}
|
||||
>
|
||||
<DownloadViewer repository={repository} file={file} />
|
||||
|
||||
@@ -24,15 +24,20 @@
|
||||
|
||||
package sonia.scm.io;
|
||||
|
||||
import com.cloudogu.spotter.ContentType;
|
||||
import com.cloudogu.spotter.ContentTypeDetector;
|
||||
import com.cloudogu.spotter.Language;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public final class DefaultContentTypeResolver implements ContentTypeResolver {
|
||||
|
||||
private final Set<ContentTypeResolverExtension> resolverExtensions;
|
||||
|
||||
private static final Language[] BOOST = new Language[]{
|
||||
// GCC Machine Description uses .md as extension, but markdown is much more likely
|
||||
Language.MARKDOWN,
|
||||
@@ -52,14 +57,25 @@ public final class DefaultContentTypeResolver implements ContentTypeResolver {
|
||||
.boost(BOOST)
|
||||
.bestEffortMatch();
|
||||
|
||||
@Inject
|
||||
public DefaultContentTypeResolver(Set<ContentTypeResolverExtension> resolverExtensions) {
|
||||
this.resolverExtensions = resolverExtensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultContentType resolve(String path) {
|
||||
return new DefaultContentType(PATH_BASED.detect(path));
|
||||
Optional<String> extensionContentType = resolveContentTypeFromExtensions(path, new byte[]{});
|
||||
return extensionContentType
|
||||
.map(rawContentType -> new DefaultContentType(new ContentType(rawContentType)))
|
||||
.orElseGet(() -> new DefaultContentType(PATH_BASED.detect(path)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultContentType resolve(String path, byte[] contentPrefix) {
|
||||
return new DefaultContentType(PATH_AND_CONTENT_BASED.detect(path, contentPrefix));
|
||||
Optional<String> extensionContentType = resolveContentTypeFromExtensions(path, contentPrefix);
|
||||
return extensionContentType
|
||||
.map(rawContentType -> new DefaultContentType(new ContentType(rawContentType)))
|
||||
.orElseGet(() -> new DefaultContentType(PATH_AND_CONTENT_BASED.detect(path, contentPrefix)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,4 +86,12 @@ public final class DefaultContentTypeResolver implements ContentTypeResolver {
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
private Optional<String> resolveContentTypeFromExtensions(String path, byte[] contentPrefix) {
|
||||
return resolverExtensions.stream()
|
||||
.map(r -> r.resolve(path, contentPrefix))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -74,7 +75,7 @@ public class ContentResourceTest {
|
||||
|
||||
@Before
|
||||
public void initService() throws Exception {
|
||||
contentResource = new ContentResource(repositoryServiceFactory, new DefaultContentTypeResolver());
|
||||
contentResource = new ContentResource(repositoryServiceFactory, new DefaultContentTypeResolver(Collections.emptySet()));
|
||||
|
||||
NamespaceAndName existingNamespaceAndName = new NamespaceAndName(NAMESPACE, REPO_NAME);
|
||||
RepositoryService repositoryService = repositoryServiceFactory.create(existingNamespaceAndName);
|
||||
|
||||
@@ -36,6 +36,7 @@ import sonia.scm.repository.api.DiffResult;
|
||||
import sonia.scm.repository.api.Hunk;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
@@ -53,7 +54,7 @@ class DiffResultToDiffResultDtoMapperTest {
|
||||
private static final Repository REPOSITORY = new Repository("1", "git", "space", "X");
|
||||
|
||||
ResourceLinks resourceLinks = ResourceLinksMock.createMock(create("/scm/api/v2"));
|
||||
DiffResultToDiffResultDtoMapper mapper = new DiffResultToDiffResultDtoMapper(resourceLinks, new DefaultContentTypeResolver());
|
||||
DiffResultToDiffResultDtoMapper mapper = new DiffResultToDiffResultDtoMapper(resourceLinks, new DefaultContentTypeResolver(Collections.emptySet()));
|
||||
|
||||
@Test
|
||||
void shouldMapDiffResult() {
|
||||
|
||||
@@ -24,19 +24,22 @@
|
||||
|
||||
package sonia.scm.io;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DefaultContentTypeResolverTest {
|
||||
|
||||
private final DefaultContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver();
|
||||
private final DefaultContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver(Collections.emptySet());
|
||||
|
||||
@Test
|
||||
void shouldReturnPrimaryPart() {
|
||||
@@ -56,6 +59,14 @@ class DefaultContentTypeResolverTest {
|
||||
assertThat(contentType.getRaw()).isEqualTo("application/pdf");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnContentTypeFromExtension() {
|
||||
DefaultContentTypeResolver contentTypeResolver = new DefaultContentTypeResolver(ImmutableSet.of((path, contentPrefix) -> Optional.of("scm/test")));
|
||||
|
||||
ContentType contentType = contentTypeResolver.resolve("hog.pdf");
|
||||
assertThat(contentType.getRaw()).isEqualTo("scm/test");
|
||||
}
|
||||
|
||||
@Nested
|
||||
class IsTextTests {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user