From 954393d69d65c4496f2cbb3e680eb2424eecfd23 Mon Sep 17 00:00:00 2001 From: amvanbaren Date: Fri, 10 Jun 2022 12:21:28 +0200 Subject: [PATCH] Use [Content_Types].xml to set FileResource contentType --- server/build.gradle | 4 +- .../eclipse/openvsx/ExtensionProcessor.java | 59 +++++++++++++++++-- .../eclipse/openvsx/LocalRegistryService.java | 2 +- .../openvsx/adapter/VSCodeAdapter.java | 4 +- .../eclipse/openvsx/dto/FileResourceDTO.java | 18 ++++-- .../openvsx/entities/FileResource.java | 10 ++++ .../FileResourceDTORepository.java | 17 +++++- .../storage/AzureBlobStorageService.java | 14 +++-- .../storage/GoogleCloudStorageService.java | 14 ++--- .../eclipse/openvsx/storage/StorageUtil.java | 21 ------- .../openvsx/storage/StorageUtilService.java | 17 ++++-- .../V1_25__FileResource_ContentType.sql | 1 + .../org/apache/tika/mime/custom-mimetypes.xml | 7 --- .../org/eclipse/openvsx/RegistryAPITest.java | 3 + .../openvsx/adapter/VSCodeAdapterTest.java | 43 +++++++++----- 15 files changed, 154 insertions(+), 80 deletions(-) create mode 100644 server/src/main/resources/db/migration/V1_25__FileResource_ContentType.sql delete mode 100644 server/src/main/resources/org/apache/tika/mime/custom-mimetypes.xml diff --git a/server/build.gradle b/server/build.gradle index 032db0ebb..0c145ecab 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -29,8 +29,7 @@ def versions = [ junit: '5.7.1', testcontainers: '1.15.2', jackson: '2.12.5', - woodstox: '6.2.4', - tika: '2.4.0' + woodstox: '6.2.4' ] ext['junit-jupiter.version'] = versions.junit sourceCompatibility = versions.java @@ -67,7 +66,6 @@ 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}" diff --git a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java index 278cf3f77..8bdd70250 100644 --- a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java +++ b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java @@ -9,10 +9,8 @@ ********************************************************************************/ package org.eclipse.openvsx; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -38,6 +36,11 @@ import org.slf4j.LoggerFactory; import org.springframework.data.util.Pair; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; /** * Processes uploaded extension files and extracts their metadata. @@ -291,6 +294,8 @@ private List getEngines(JsonNode node) { public List getResources(ExtensionVersion extension) { var resources = new ArrayList<>(getAllResources(extension)); + var contentTypes = loadContentTypes(resources); + var binary = getBinary(extension); if (binary != null) resources.add(binary); @@ -310,7 +315,9 @@ public List getResources(ExtensionVersion extension) { if (icon != null) resources.add(icon); - return resources; + return resources.stream() + .map(resource -> setContentType(resource, contentTypes)) + .collect(Collectors.toList()); } protected List getAllResources(ExtensionVersion extension) { @@ -421,6 +428,48 @@ protected FileResource getLicense(ExtensionVersion extension) { return license; } + private Map loadContentTypes(List resources) { + var contentTypes = resources.stream() + .filter(r -> r.getName().equals("[Content_Types].xml")) + .findFirst() + .map(FileResource::getContent) + .map(this::parseContentTypesXml) + .orElse(new HashMap<>()); + + contentTypes.putIfAbsent(".vsix", "application/zip"); + return contentTypes; + } + + private Map parseContentTypesXml(byte[] content) { + try (var input = new ByteArrayInputStream(content)) { + var contentTypes = new HashMap(); + var document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input); + var elements = document.getDocumentElement().getElementsByTagName("Default"); + for(var i = 0; i < elements.getLength(); i++) { + var element = elements.item(i); + var attributes = element.getAttributes(); + var extension = attributes.getNamedItem("Extension").getTextContent(); + var contentType = attributes.getNamedItem("ContentType").getTextContent(); + contentTypes.put(extension, contentType); + } + + return contentTypes; + } catch (IOException | ParserConfigurationException | SAXException e) { + logger.error("failed to read content types", e); + return null; + } + } + + private FileResource setContentType(FileResource resource, Map contentTypes) { + var resourceName = Optional.ofNullable(resource.getName()).orElse(""); + var fileExtensionIndex = resourceName.lastIndexOf('.'); + var fileExtension = fileExtensionIndex != -1 ? resourceName.substring(fileExtensionIndex) : ""; + var contentType = contentTypes.getOrDefault(fileExtension, MediaType.APPLICATION_OCTET_STREAM_VALUE); + + resource.setContentType(contentType); + return resource; + } + private void detectLicense(byte[] content, ExtensionVersion extension) { if (Strings.isNullOrEmpty(extension.getLicense())) { var detection = new LicenseDetection(); diff --git a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java index 72f6b3d83..2e0c2ba4e 100644 --- a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java +++ b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java @@ -165,7 +165,7 @@ public ResponseEntity getFile(String namespace, String extensionName, St if (resource.getType().equals(DOWNLOAD)) storageUtil.increaseDownloadCount(extVersion, resource); if (resource.getStorageType().equals(FileResource.STORAGE_DB)) { - var headers = storageUtil.getFileResponseHeaders(fileName); + var headers = storageUtil.getFileResponseHeaders(resource); return new ResponseEntity<>(resource.getContent(), headers, HttpStatus.OK); } else { return ResponseEntity.status(HttpStatus.FOUND) diff --git a/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAdapter.java b/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAdapter.java index 7525efd53..612392187 100644 --- a/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAdapter.java +++ b/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAdapter.java @@ -309,7 +309,7 @@ public ResponseEntity getAsset(HttpServletRequest request, if (resource.getType().equals(FileResource.DOWNLOAD)) storageUtil.increaseDownloadCount(extVersion, resource); if (resource.getStorageType().equals(FileResource.STORAGE_DB)) { - var headers = storageUtil.getFileResponseHeaders(resource.getName()); + var headers = storageUtil.getFileResponseHeaders(resource); return new ResponseEntity<>(resource.getContent(), headers, HttpStatus.OK); } else { return ResponseEntity.status(HttpStatus.FOUND) @@ -408,7 +408,7 @@ private ResponseEntity browseFile( String version ) { if (resource.getStorageType().equals(FileResource.STORAGE_DB)) { - var headers = storageUtil.getFileResponseHeaders(resource.getName()); + var headers = storageUtil.getFileResponseHeaders(resource); return new ResponseEntity<>(resource.getContent(), headers, HttpStatus.OK); } else { var namespace = new Namespace(); diff --git a/server/src/main/java/org/eclipse/openvsx/dto/FileResourceDTO.java b/server/src/main/java/org/eclipse/openvsx/dto/FileResourceDTO.java index c545be3b6..ea57ad699 100644 --- a/server/src/main/java/org/eclipse/openvsx/dto/FileResourceDTO.java +++ b/server/src/main/java/org/eclipse/openvsx/dto/FileResourceDTO.java @@ -11,8 +11,6 @@ import org.eclipse.openvsx.entities.FileResource; -import java.util.Objects; - public class FileResourceDTO { private final long extensionVersionId; @@ -23,11 +21,21 @@ public class FileResourceDTO { private final String type; private String storageType; private byte[] content; - - public FileResourceDTO(long id, long extensionVersionId, String name, String type, String storageType, byte[] content) { + private String contentType; + + public FileResourceDTO( + long id, + long extensionVersionId, + String name, + String type, + String storageType, + byte[] content, + String contentType + ) { this(id, extensionVersionId, name, type); this.storageType = storageType; this.content = content; + this.contentType = contentType; } public FileResourceDTO(long id, long extensionVersionId, String name, String type) { @@ -73,6 +81,8 @@ public byte[] getContent() { return content; } + public String getContentType() { return contentType; } + public boolean isWebResource() { return type.equals(FileResource.RESOURCE) && name.startsWith("extension/"); } diff --git a/server/src/main/java/org/eclipse/openvsx/entities/FileResource.java b/server/src/main/java/org/eclipse/openvsx/entities/FileResource.java index 0aa68ca10..bca9063a4 100644 --- a/server/src/main/java/org/eclipse/openvsx/entities/FileResource.java +++ b/server/src/main/java/org/eclipse/openvsx/entities/FileResource.java @@ -46,6 +46,8 @@ public class FileResource { byte[] content; + String contentType; + @Column(length = 32) String storageType; @@ -90,6 +92,14 @@ public void setContent(byte[] content) { this.content = content; } + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + public String getStorageType() { return storageType; } diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/FileResourceDTORepository.java b/server/src/main/java/org/eclipse/openvsx/repositories/FileResourceDTORepository.java index 9e545b98a..d04cacceb 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/FileResourceDTORepository.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/FileResourceDTORepository.java @@ -30,7 +30,12 @@ public class FileResourceDTORepository { DSLContext dsl; public List findAll(Collection extensionIds, Collection types) { - return dsl.select(FILE_RESOURCE.ID, FILE_RESOURCE.EXTENSION_ID, FILE_RESOURCE.NAME, FILE_RESOURCE.TYPE) + return dsl.select( + FILE_RESOURCE.ID, + FILE_RESOURCE.EXTENSION_ID, + FILE_RESOURCE.NAME, + FILE_RESOURCE.TYPE + ) .from(FILE_RESOURCE) .where(FILE_RESOURCE.EXTENSION_ID.in(extensionIds)) .and(FILE_RESOURCE.TYPE.in(types)) @@ -38,7 +43,15 @@ public List findAll(Collection extensionIds, Collection findAllResources(String namespaceName, String extensionName, String version, String prefix) { - return dsl.select(FILE_RESOURCE.ID, FILE_RESOURCE.EXTENSION_ID, FILE_RESOURCE.NAME, FILE_RESOURCE.TYPE, FILE_RESOURCE.STORAGE_TYPE, FILE_RESOURCE.CONTENT) + return dsl.select( + FILE_RESOURCE.ID, + FILE_RESOURCE.EXTENSION_ID, + FILE_RESOURCE.NAME, + FILE_RESOURCE.TYPE, + FILE_RESOURCE.STORAGE_TYPE, + FILE_RESOURCE.CONTENT, + FILE_RESOURCE.CONTENT_TYPE + ) .from(FILE_RESOURCE) .join(EXTENSION_VERSION).on(EXTENSION_VERSION.ID.eq(FILE_RESOURCE.EXTENSION_ID)) .join(EXTENSION).on(EXTENSION.ID.eq(EXTENSION_VERSION.EXTENSION_ID)) diff --git a/server/src/main/java/org/eclipse/openvsx/storage/AzureBlobStorageService.java b/server/src/main/java/org/eclipse/openvsx/storage/AzureBlobStorageService.java index 78c169193..49bf56cc4 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/AzureBlobStorageService.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/AzureBlobStorageService.java @@ -64,19 +64,21 @@ public void uploadFile(FileResource resource) { + blobName + ": missing Azure blob service endpoint"); } - uploadFile(resource.getContent(), resource.getName(), blobName); + uploadFile(resource, blobName); } - protected void uploadFile(byte[] content, String fileName, String blobName) { + protected void uploadFile(FileResource resource, String blobName) { var blobClient = getContainerClient().getBlobClient(blobName); var headers = new BlobHttpHeaders(); - headers.setContentType(StorageUtil.getFileType(fileName).toString()); - if (fileName.endsWith(".vsix")) { - headers.setContentDisposition("attachment; filename=\"" + fileName + "\""); + headers.setContentType(resource.getContentType()); + if (resource.getName().endsWith(".vsix")) { + headers.setContentDisposition("attachment; filename=\"" + resource.getName() + "\""); } else { - var cacheControl = StorageUtil.getCacheControl(fileName); + var cacheControl = StorageUtil.getCacheControl(resource.getName()); headers.setCacheControl(cacheControl.getHeaderValue()); } + + var content = resource.getContent(); try (var dataStream = new ByteArrayInputStream(content)) { blobClient.upload(dataStream, content.length, true); blobClient.setHttpHeaders(headers); diff --git a/server/src/main/java/org/eclipse/openvsx/storage/GoogleCloudStorageService.java b/server/src/main/java/org/eclipse/openvsx/storage/GoogleCloudStorageService.java index 979ea8ceb..8c74966ed 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/GoogleCloudStorageService.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/GoogleCloudStorageService.java @@ -64,19 +64,19 @@ public void uploadFile(FileResource resource) { + objectId + ": missing Google bucket id"); } - uploadFile(resource.getContent(), resource.getName(), objectId); + uploadFile(resource, objectId); } - protected void uploadFile(byte[] content, String fileName, String objectId) { + protected void uploadFile(FileResource resource, String objectId) { var blobInfoBuilder = BlobInfo.newBuilder(BlobId.of(bucketId, objectId)) - .setContentType(StorageUtil.getFileType(fileName).toString()); - if (fileName.endsWith(".vsix")) { - blobInfoBuilder.setContentDisposition("attachment; filename=\"" + fileName + "\""); + .setContentType(resource.getContentType()); + if (resource.getName().endsWith(".vsix")) { + blobInfoBuilder.setContentDisposition("attachment; filename=\"" + resource.getName() + "\""); } else { - var cacheControl = StorageUtil.getCacheControl(fileName); + var cacheControl = StorageUtil.getCacheControl(resource.getName()); blobInfoBuilder.setCacheControl(cacheControl.getHeaderValue()); } - getStorage().create(blobInfoBuilder.build(), content); + getStorage().create(blobInfoBuilder.build(), resource.getContent()); } @Override diff --git a/server/src/main/java/org/eclipse/openvsx/storage/StorageUtil.java b/server/src/main/java/org/eclipse/openvsx/storage/StorageUtil.java index b34b7a097..a5b83c804 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/StorageUtil.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/StorageUtil.java @@ -10,35 +10,14 @@ package org.eclipse.openvsx.storage; -import org.apache.tika.Tika; import org.springframework.http.CacheControl; -import org.springframework.http.MediaType; -import java.net.URLConnection; import java.util.concurrent.TimeUnit; class StorageUtil { private StorageUtil(){} - @Deprecated - static MediaType getFileTypeDeprecated(String fileName) { - if (fileName.endsWith(".vsix")) - return MediaType.APPLICATION_OCTET_STREAM; - if (fileName.endsWith(".json")) - return MediaType.APPLICATION_JSON; - var contentType = URLConnection.guessContentTypeFromName(fileName); - if (contentType != null) - return MediaType.parseMediaType(contentType); - 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(); diff --git a/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java b/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java index 3e2a4c033..5510886bb 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java @@ -12,18 +12,16 @@ import static org.eclipse.openvsx.entities.FileResource.*; import java.net.URI; -import java.net.URLConnection; import java.time.LocalDateTime; import java.util.*; -import java.util.concurrent.TimeUnit; import javax.persistence.EntityManager; import javax.transaction.Transactional; import com.google.common.base.Strings; -import org.apache.commons.lang3.ArrayUtils; import org.eclipse.openvsx.cache.CacheService; +import org.eclipse.openvsx.dto.FileResourceDTO; import org.eclipse.openvsx.entities.Download; import org.eclipse.openvsx.entities.ExtensionVersion; import org.eclipse.openvsx.entities.FileResource; @@ -33,7 +31,6 @@ import org.eclipse.openvsx.util.UrlUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.CacheControl; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -195,9 +192,17 @@ public void increaseDownloadCount(ExtensionVersion extVersion, FileResource reso cache.evictExtensionJsons(extension); } - public HttpHeaders getFileResponseHeaders(String fileName) { + public HttpHeaders getFileResponseHeaders(FileResource resource) { + return getFileResponseHeaders(resource.getName(), resource.getContentType()); + } + + public HttpHeaders getFileResponseHeaders(FileResourceDTO resource) { + return getFileResponseHeaders(resource.getName(), resource.getContentType()); + } + + private HttpHeaders getFileResponseHeaders(String fileName, String contentType) { var headers = new HttpHeaders(); - headers.setContentType(StorageUtil.getFileType(fileName)); + headers.setContentType(MediaType.parseMediaType(contentType)); if (fileName.endsWith(".vsix")) { headers.add("Content-Disposition", "attachment; filename=\"" + fileName + "\""); } else { diff --git a/server/src/main/resources/db/migration/V1_25__FileResource_ContentType.sql b/server/src/main/resources/db/migration/V1_25__FileResource_ContentType.sql new file mode 100644 index 000000000..3795dc37c --- /dev/null +++ b/server/src/main/resources/db/migration/V1_25__FileResource_ContentType.sql @@ -0,0 +1 @@ +ALTER TABLE file_resource ADD COLUMN content_type CHARACTER VARYING(255); \ No newline at end of file diff --git a/server/src/main/resources/org/apache/tika/mime/custom-mimetypes.xml b/server/src/main/resources/org/apache/tika/mime/custom-mimetypes.xml deleted file mode 100644 index 5255ec2b7..000000000 --- a/server/src/main/resources/org/apache/tika/mime/custom-mimetypes.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - <_comment>VSIX package - - - \ No newline at end of file diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index be97f474c..95b306e64 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -1415,6 +1415,7 @@ private FileResource mockReadme(String targetPlatform) { resource.setName("README"); resource.setType(FileResource.README); resource.setContent("Please read me".getBytes()); + resource.setContentType(MediaType.TEXT_PLAIN_VALUE); resource.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByName(extVersion, "README")) .thenReturn(resource); @@ -1428,6 +1429,7 @@ private FileResource mockChangelog() { resource.setName("CHANGELOG"); resource.setType(FileResource.CHANGELOG); resource.setContent("All notable changes is documented here".getBytes()); + resource.setContentType(MediaType.TEXT_PLAIN_VALUE); resource.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByName(extVersion, "CHANGELOG")) .thenReturn(resource); @@ -1441,6 +1443,7 @@ private FileResource mockLicense() { resource.setName("LICENSE"); resource.setType(FileResource.LICENSE); resource.setContent("I never broke the Law! I am the law!".getBytes()); + resource.setContentType(MediaType.TEXT_PLAIN_VALUE); resource.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByName(extVersion, "LICENSE")) .thenReturn(resource); diff --git a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAdapterTest.java b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAdapterTest.java index c3a3c74ef..52098b3e2 100644 --- a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAdapterTest.java +++ b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAdapterTest.java @@ -253,7 +253,7 @@ public void testAssetVsixPackage() throws Exception { 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")); + .andExpect(header().string(HttpHeaders.CONTENT_TYPE, "application/zip")); } @Test @@ -332,12 +332,12 @@ public void testBrowseNotFound() throws Exception { @Test public void testBrowseTopDir() throws Exception { - var vsixResource = new FileResourceDTO(15, 1, "extension.vsixmanifest", RESOURCE, STORAGE_DB, "".getBytes(StandardCharsets.UTF_8)); - var manifestResource = new FileResourceDTO(16, 1, "extension/package.json", RESOURCE, STORAGE_DB, "{\"package\":\"json\"}".getBytes(StandardCharsets.UTF_8)); - var readmeResource = new FileResourceDTO(17, 1, "extension/README.md", RESOURCE, STORAGE_DB, "README".getBytes(StandardCharsets.UTF_8)); - var changelogResource = new FileResourceDTO(18, 1, "extension/CHANGELOG.md", RESOURCE, STORAGE_DB, "CHANGELOG".getBytes(StandardCharsets.UTF_8)); - var licenseResource = new FileResourceDTO(19, 1, "extension/LICENSE.txt", RESOURCE, STORAGE_DB, "LICENSE".getBytes(StandardCharsets.UTF_8)); - var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8)); + var vsixResource = new FileResourceDTO(15, 1, "extension.vsixmanifest", RESOURCE, STORAGE_DB, "".getBytes(StandardCharsets.UTF_8), "application/xml"); + var manifestResource = new FileResourceDTO(16, 1, "extension/package.json", RESOURCE, STORAGE_DB, "{\"package\":\"json\"}".getBytes(StandardCharsets.UTF_8), "application/json"); + var readmeResource = new FileResourceDTO(17, 1, "extension/README.md", RESOURCE, STORAGE_DB, "README".getBytes(StandardCharsets.UTF_8), "text/markdown"); + var changelogResource = new FileResourceDTO(18, 1, "extension/CHANGELOG.md", RESOURCE, STORAGE_DB, "CHANGELOG".getBytes(StandardCharsets.UTF_8), "text/markdown"); + var licenseResource = new FileResourceDTO(19, 1, "extension/LICENSE.txt", RESOURCE, STORAGE_DB, "LICENSE".getBytes(StandardCharsets.UTF_8), "text/plain"); + var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8), "image/png"); Mockito.when(repositories.findAllResourceFileResourceDTOs("foo", "bar", "1.3.4", "")) .thenReturn(List.of(vsixResource, manifestResource, readmeResource, changelogResource, licenseResource, iconResource)); @@ -350,7 +350,7 @@ public void testBrowseTopDir() throws Exception { @Test public void testBrowseVsixManifest() throws Exception { var content = "".getBytes(StandardCharsets.UTF_8); - var vsixResource = new FileResourceDTO(15, 1, "extension.vsixmanifest", RESOURCE, STORAGE_DB, content); + var vsixResource = new FileResourceDTO(15, 1, "extension.vsixmanifest", RESOURCE, STORAGE_DB, content, "application/xml"); Mockito.when(repositories.findAllResourceFileResourceDTOs("foo", "bar", "1.3.4", "extension.vsixmanifest")) .thenReturn(List.of(vsixResource)); @@ -361,11 +361,11 @@ public void testBrowseVsixManifest() throws Exception { @Test public void testBrowseExtensionDir() throws Exception { - var manifestResource = new FileResourceDTO(16, 1, "extension/package.json", RESOURCE, STORAGE_DB, "{\"package\":\"json\"}".getBytes(StandardCharsets.UTF_8)); - var readmeResource = new FileResourceDTO(17, 1, "extension/README.md", RESOURCE, STORAGE_DB, "README".getBytes(StandardCharsets.UTF_8)); - var changelogResource = new FileResourceDTO(18, 1, "extension/CHANGELOG.md", RESOURCE, STORAGE_DB, "CHANGELOG".getBytes(StandardCharsets.UTF_8)); - var licenseResource = new FileResourceDTO(19, 1, "extension/LICENSE.txt", RESOURCE, STORAGE_DB, "LICENSE".getBytes(StandardCharsets.UTF_8)); - var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8)); + var manifestResource = new FileResourceDTO(16, 1, "extension/package.json", RESOURCE, STORAGE_DB, "{\"package\":\"json\"}".getBytes(StandardCharsets.UTF_8), "application/json"); + var readmeResource = new FileResourceDTO(17, 1, "extension/README.md", RESOURCE, STORAGE_DB, "README".getBytes(StandardCharsets.UTF_8), "text/markdown"); + var changelogResource = new FileResourceDTO(18, 1, "extension/CHANGELOG.md", RESOURCE, STORAGE_DB, "CHANGELOG".getBytes(StandardCharsets.UTF_8), "text/markdown"); + var licenseResource = new FileResourceDTO(19, 1, "extension/LICENSE.txt", RESOURCE, STORAGE_DB, "LICENSE".getBytes(StandardCharsets.UTF_8), "text/plain"); + var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8), "image/png"); Mockito.when(repositories.findAllResourceFileResourceDTOs("foo", "bar", "1.3.4", "extension")) .thenReturn(List.of(manifestResource, readmeResource, changelogResource, licenseResource, iconResource)); @@ -385,7 +385,7 @@ public void testBrowseExtensionDir() throws Exception { @Test public void testBrowsePackageJson() throws Exception { var content = "{\"package\":\"json\"}".getBytes(StandardCharsets.UTF_8); - var manifestResource = new FileResourceDTO(16, 1, "extension/package.json", RESOURCE, STORAGE_DB, content); + var manifestResource = new FileResourceDTO(16, 1, "extension/package.json", RESOURCE, STORAGE_DB, content, "application/json"); Mockito.when(repositories.findAllResourceFileResourceDTOs("foo", "bar", "1.3.4", "extension/package.json")) .thenReturn(List.of(manifestResource)); @@ -396,7 +396,7 @@ public void testBrowsePackageJson() throws Exception { @Test public void testBrowseImagesDir() throws Exception { - var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8)); + var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8), "image/png"); Mockito.when(repositories.findAllResourceFileResourceDTOs("foo", "bar", "1.3.4", "extension/images")) .thenReturn(List.of(iconResource)); @@ -409,7 +409,7 @@ public void testBrowseImagesDir() throws Exception { @Test public void testBrowseIcon() throws Exception { var content = "ICON128".getBytes(StandardCharsets.UTF_8); - var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, content); + var iconResource = new FileResourceDTO(20, 1, "extension/images/icon128.png", RESOURCE, STORAGE_DB, content, "image/png"); Mockito.when(repositories.findAllResourceFileResourceDTOs("foo", "bar", "1.3.4", "extension/images/icon128.png")) .thenReturn(List.of(iconResource)); @@ -582,6 +582,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess extensionFile.setExtension(extVersion); extensionFile.setName("redhat.vscode-yaml-0.5.2.vsix"); extensionFile.setType(FileResource.DOWNLOAD); + extensionFile.setContentType("application/zip"); extensionFile.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByType(extVersion, FileResource.DOWNLOAD)) .thenReturn(extensionFile); @@ -594,6 +595,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess if(!targetPlatform.equals(TargetPlatform.NAME_UNIVERSAL)) manifestContent.put("target", targetPlatform); manifestFile.setContent(new ObjectMapper().writeValueAsBytes(manifestContent)); + manifestFile.setContentType(MediaType.APPLICATION_JSON_VALUE); manifestFile.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByType(extVersion, FileResource.MANIFEST)) .thenReturn(manifestFile); @@ -601,6 +603,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess readmeFile.setExtension(extVersion); readmeFile.setName("README.md"); readmeFile.setType(FileResource.README); + readmeFile.setContentType("text/markdown"); readmeFile.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByType(extVersion, FileResource.README)) .thenReturn(readmeFile); @@ -608,6 +611,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess changelogFile.setExtension(extVersion); changelogFile.setName("CHANGELOG.md"); changelogFile.setType(FileResource.CHANGELOG); + changelogFile.setContentType("text/markdown"); changelogFile.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByType(extVersion, FileResource.CHANGELOG)) .thenReturn(changelogFile); @@ -615,6 +619,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess licenseFile.setExtension(extVersion); licenseFile.setName("LICENSE.txt"); licenseFile.setType(FileResource.LICENSE); + licenseFile.setContentType(MediaType.TEXT_PLAIN_VALUE); licenseFile.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByType(extVersion, FileResource.LICENSE)) .thenReturn(licenseFile); @@ -622,6 +627,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess iconFile.setExtension(extVersion); iconFile.setName("icon128.png"); iconFile.setType(FileResource.ICON); + iconFile.setContentType(MediaType.IMAGE_PNG_VALUE); iconFile.setStorageType(FileResource.STORAGE_DB); Mockito.when(repositories.findFileByType(extVersion, FileResource.ICON)) .thenReturn(iconFile); @@ -631,6 +637,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess pngWebResourceFile.setType(FileResource.RESOURCE); pngWebResourceFile.setStorageType(STORAGE_DB); pngWebResourceFile.setContent("logo.png".getBytes()); + pngWebResourceFile.setContentType(MediaType.IMAGE_PNG_VALUE); Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/img/logo.png")) .thenReturn(pngWebResourceFile); var jsWebResourceFile = new FileResource(); @@ -639,6 +646,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess jsWebResourceFile.setType(FileResource.RESOURCE); jsWebResourceFile.setStorageType(STORAGE_DB); jsWebResourceFile.setContent("() => { console.log('main'); }".getBytes()); + jsWebResourceFile.setContentType("application/javascript"); Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/js/main.js")) .thenReturn(jsWebResourceFile); var jsChunkWebResourceFile = new FileResource(); @@ -647,6 +655,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess jsChunkWebResourceFile.setType(FileResource.RESOURCE); jsChunkWebResourceFile.setStorageType(STORAGE_DB); jsChunkWebResourceFile.setContent("() => { console.log('js'); }".getBytes()); + jsChunkWebResourceFile.setContentType("application/javascript"); Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/js/main.34d01954.chunk.js")) .thenReturn(jsChunkWebResourceFile); var cssWebResourceFile = new FileResource(); @@ -655,6 +664,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess cssWebResourceFile.setType(FileResource.RESOURCE); cssWebResourceFile.setStorageType(STORAGE_DB); cssWebResourceFile.setContent(".main { margin: 0 auto; }".getBytes()); + cssWebResourceFile.setContentType("text/css"); Mockito.when(repositories.findFileByTypeAndName(extVersion, FileResource.RESOURCE, "extension/public/static/css/main.css")) .thenReturn(cssWebResourceFile); var cssChunkWebResourceFile = new FileResource(); @@ -663,6 +673,7 @@ private ExtensionVersion mockExtension(String targetPlatform) throws JsonProcess cssChunkWebResourceFile.setType(FileResource.RESOURCE); cssChunkWebResourceFile.setStorageType(STORAGE_DB); cssChunkWebResourceFile.setContent(".root { margin: 0 auto; }".getBytes()); + cssChunkWebResourceFile.setContentType("text/css"); 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)))