Skip to content

Commit

Permalink
Web extension resources return incorrect MIME type
Browse files Browse the repository at this point in the history
Contributes to #468
Use Apache Tika to detect mimetype
  • Loading branch information
amvanbaren committed Jun 9, 2022
1 parent 72706d1 commit fb56cee
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 12 deletions.
4 changes: 3 additions & 1 deletion server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def versions = [
junit: '5.7.1',
testcontainers: '1.15.2',
jackson: '2.12.5',
woodstox: '6.2.4'
woodstox: '6.2.4',
tika: '2.4.0'
]
ext['junit-jupiter.version'] = versions.junit
sourceCompatibility = versions.java
Expand Down Expand Up @@ -66,6 +67,7 @@ dependencies {
implementation "org.springframework.security:spring-security-oauth2-jose"
implementation "org.springframework.session:spring-session-jdbc"
implementation "org.flywaydb:flyway-core:${versions.flyway}"
implementation "org.apache.tika:tika-core:${versions.tika}"
implementation "com.google.cloud:google-cloud-storage:${versions.gcloud}"
implementation "com.azure:azure-storage-blob:${versions.azure}"
implementation "io.springfox:springfox-boot-starter:${versions.springfox}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package org.eclipse.openvsx.storage;

import org.apache.tika.Tika;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;

Expand All @@ -20,7 +21,8 @@ class StorageUtil {

private StorageUtil(){}

static MediaType getFileType(String fileName) {
@Deprecated
static MediaType getFileTypeDeprecated(String fileName) {
if (fileName.endsWith(".vsix"))
return MediaType.APPLICATION_OCTET_STREAM;
if (fileName.endsWith(".json"))
Expand All @@ -31,6 +33,12 @@ static MediaType getFileType(String fileName) {
return MediaType.TEXT_PLAIN;
}

static MediaType getFileType(String fileName) {
var tika = new Tika();
var contentType = tika.detect(fileName); // returns 'application/octet-stream' by default
return MediaType.parseMediaType(contentType);
}

static CacheControl getCacheControl(String fileName) {
// Files are requested with a version string in the URL, so their content cannot change
return CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info>
<mime-type type="application/vsix">
<_comment>VSIX package</_comment>
<glob pattern="*.vsix"/>
</mime-type>
</mime-info>
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.io.CharStreams;

Expand All @@ -50,7 +49,6 @@
import org.eclipse.openvsx.storage.GoogleCloudStorageService;
import org.eclipse.openvsx.storage.StorageUtilService;
import org.eclipse.openvsx.util.TargetPlatform;
import org.eclipse.openvsx.util.VersionUtil;
import org.elasticsearch.search.aggregations.Aggregations;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
Expand All @@ -65,6 +63,7 @@
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.util.Streamable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.test.web.servlet.MockMvc;
Expand Down Expand Up @@ -223,6 +222,7 @@ public void testAsset() throws Exception {
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.Manifest"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
.andExpect(content().string("{\"foo\":\"bar\"}"));
}

Expand All @@ -233,6 +233,7 @@ public void testAssetMacOSX() throws Exception {
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}?targetPlatform={target}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.Manifest", target))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
.andExpect(content().string("{\"foo\":\"bar\",\"target\":\"darwin-arm64\"}"));
}

Expand All @@ -246,6 +247,15 @@ public void testAssetNotFound() throws Exception {
.andExpect(status().isNotFound());
}

@Test
public void testAssetVsixPackage() throws Exception {
mockExtension();
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Services.VSIXPackage"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "application/vsix"));
}

@Test
public void testGetItem() throws Exception {
mockMvc.perform(get("/vscode/item?itemName={itemName}", "redhat.vscode-yaml"))
Expand All @@ -254,14 +264,55 @@ public void testGetItem() throws Exception {
}

@Test
public void testWebResourceAsset() throws Exception {
public void testPngWebResourceAsset() throws Exception {
mockExtension();
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/img/logo.png"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.IMAGE_PNG_VALUE))
.andExpect(content().string("logo.png"));
}

@Test
public void testCssWebResourceAsset() throws Exception {
mockExtension();
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/css/main.css"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "text/css"))
.andExpect(content().string(".main { margin: 0 auto; }"));
}

@Test
public void testChunkCssWebResourceAsset() throws Exception {
mockExtension();
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/css/main.9cab4879.chunk.css"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "text/css"))
.andExpect(content().string(".root { margin: 0 auto; }"));
}

@Test
public void testJsWebResourceAsset() throws Exception {
mockExtension();
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/js/main.js"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "application/javascript"))
.andExpect(content().string("() => { console.log('main'); }"));
}

@Test
public void testChunkJsWebResourceAsset() throws Exception {
mockExtension();
mockMvc.perform(get("/vscode/asset/{namespace}/{extensionName}/{version}/{assetType}",
"redhat", "vscode-yaml", "0.5.2", "Microsoft.VisualStudio.Code.WebResources/extension/public/static/js/main.34d01954.chunk.js"))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, "application/javascript"))
.andExpect(content().string("() => { console.log('js'); }"));
}

@Test
public void testNotWebResourceAsset() throws Exception {
mockExtension();
Expand Down Expand Up @@ -502,6 +553,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess
extension.setAverageRating(3.0);
extension.setNamespace(namespace);
var extVersion = new ExtensionVersion();
extVersion.setExtension(extension);
extension.getVersions().add(extVersion);
extVersion.setTargetPlatform(targetPlatform);
extVersion.setVersion("0.5.2");
Expand Down Expand Up @@ -573,14 +625,46 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess
iconFile.setStorageType(FileResource.STORAGE_DB);
Mockito.when(repositories.findFileByType(extVersion, FileResource.ICON))
.thenReturn(iconFile);
var webResourceFile = new FileResource();
webResourceFile.setExtension(extVersion);
webResourceFile.setName("extension/img/logo.png");
webResourceFile.setType(FileResource.RESOURCE);
webResourceFile.setStorageType(STORAGE_DB);
webResourceFile.setContent("logo.png".getBytes());
var pngWebResourceFile = new FileResource();
pngWebResourceFile.setExtension(extVersion);
pngWebResourceFile.setName("extension/img/logo.png");
pngWebResourceFile.setType(FileResource.RESOURCE);
pngWebResourceFile.setStorageType(STORAGE_DB);
pngWebResourceFile.setContent("logo.png".getBytes());
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/img/logo.png"))
.thenReturn(webResourceFile);
.thenReturn(pngWebResourceFile);
var jsWebResourceFile = new FileResource();
jsWebResourceFile.setExtension(extVersion);
jsWebResourceFile.setName("extension/public/static/js/main.js");
jsWebResourceFile.setType(FileResource.RESOURCE);
jsWebResourceFile.setStorageType(STORAGE_DB);
jsWebResourceFile.setContent("() => { console.log('main'); }".getBytes());
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/js/main.js"))
.thenReturn(jsWebResourceFile);
var jsChunkWebResourceFile = new FileResource();
jsChunkWebResourceFile.setExtension(extVersion);
jsChunkWebResourceFile.setName("extension/public/static/js/main.34d01954.chunk.js");
jsChunkWebResourceFile.setType(FileResource.RESOURCE);
jsChunkWebResourceFile.setStorageType(STORAGE_DB);
jsChunkWebResourceFile.setContent("() => { console.log('js'); }".getBytes());
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/js/main.34d01954.chunk.js"))
.thenReturn(jsChunkWebResourceFile);
var cssWebResourceFile = new FileResource();
cssWebResourceFile.setExtension(extVersion);
cssWebResourceFile.setName("extension/public/static/css/main.css");
cssWebResourceFile.setType(FileResource.RESOURCE);
cssWebResourceFile.setStorageType(STORAGE_DB);
cssWebResourceFile.setContent(".main { margin: 0 auto; }".getBytes());
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/css/main.css"))
.thenReturn(cssWebResourceFile);
var cssChunkWebResourceFile = new FileResource();
cssChunkWebResourceFile.setExtension(extVersion);
cssChunkWebResourceFile.setName("extension/public/static/css/main.9cab4879.chunk.css");
cssChunkWebResourceFile.setType(FileResource.RESOURCE);
cssChunkWebResourceFile.setStorageType(STORAGE_DB);
cssChunkWebResourceFile.setContent(".root { margin: 0 auto; }".getBytes());
Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/css/main.9cab4879.chunk.css"))
.thenReturn(cssChunkWebResourceFile);
Mockito.when(repositories.findFilesByType(extVersion, Arrays.asList(FileResource.MANIFEST, FileResource.README, FileResource.LICENSE, FileResource.ICON, FileResource.DOWNLOAD, FileResource.CHANGELOG)))
.thenReturn(Streamable.of(manifestFile, readmeFile, licenseFile, iconFile, extensionFile, changelogFile));
return extVersion;
Expand Down

0 comments on commit fb56cee

Please sign in to comment.