Skip to content

Commit 5e75785

Browse files
author
amvanbaren
committed
Adopt to new web platform specifier extension resource url format
Added target to unpkg endpoint Added unit tests
1 parent 63eae3e commit 5e75785

File tree

8 files changed

+210
-10
lines changed

8 files changed

+210
-10
lines changed

server/src/main/java/org/eclipse/openvsx/adapter/IVSCodeService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public interface IVSCodeService {
1515

1616
ExtensionQueryResult extensionQuery(ExtensionQueryParam param, int defaultPageSize);
1717

18-
ResponseEntity<byte[]> browse(String namespaceName, String extensionName, String version, String path);
18+
ResponseEntity<byte[]> browse(String namespaceName, String extensionName, String version, String targetPlatform, String path);
1919

2020
String download(String namespace, String extension, String version, String targetPlatform);
2121

server/src/main/java/org/eclipse/openvsx/adapter/LocalVSCodeService.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.fasterxml.jackson.core.JsonProcessingException;
1313
import com.fasterxml.jackson.databind.ObjectMapper;
1414
import com.google.common.collect.Lists;
15+
import org.apache.commons.lang3.StringUtils;
1516
import org.eclipse.openvsx.entities.Extension;
1617
import org.eclipse.openvsx.entities.ExtensionVersion;
1718
import org.eclipse.openvsx.entities.FileResource;
@@ -386,15 +387,20 @@ public String download(String namespace, String extension, String version, Strin
386387
}
387388

388389
@Override
389-
public ResponseEntity<byte[]> browse(String namespaceName, String extensionName, String version, String path) {
390+
public ResponseEntity<byte[]> browse(String namespaceName, String extensionName, String version, String targetPlatform, String path) {
390391
if(isBuiltInExtensionNamespace(namespaceName)) {
391392
return new ResponseEntity<>(("Built-in extension namespace '" + namespaceName + "' not allowed").getBytes(StandardCharsets.UTF_8), null, HttpStatus.BAD_REQUEST);
392393
}
393394

394-
var extVersions = repositories.findActiveExtensionVersionsByVersion(version, extensionName, namespaceName);
395-
var extVersion = extVersions.stream().max(Comparator.<ExtensionVersion, Boolean>comparing(ExtensionVersion::isUniversalTargetPlatform)
396-
.thenComparing(ExtensionVersion::getTargetPlatform))
397-
.orElse(null);
395+
ExtensionVersion extVersion;
396+
if(StringUtils.isEmpty(targetPlatform)) {
397+
var extVersions = repositories.findActiveExtensionVersionsByVersion(version, extensionName, namespaceName);
398+
extVersion = extVersions.stream().max(Comparator.<ExtensionVersion, Boolean>comparing(ExtensionVersion::isUniversalTargetPlatform)
399+
.thenComparing(ExtensionVersion::getTargetPlatform))
400+
.orElse(null);
401+
} else {
402+
extVersion = repositories.findActiveExtensionVersionByVersion(version, targetPlatform, extensionName, namespaceName);
403+
}
398404

399405
if (extVersion == null) {
400406
throw new NotFoundException();
@@ -412,7 +418,7 @@ public ResponseEntity<byte[]> browse(String namespaceName, String extensionName,
412418

413419
return exactMatch != null
414420
? browseFile(exactMatch, namespaceName, extensionName, extVersion.getTargetPlatform(), version)
415-
: browseDirectory(resources, namespaceName, extensionName, version, path);
421+
: browseDirectory(resources, namespaceName, extensionName, targetPlatform, version, path);
416422
}
417423

418424
private ResponseEntity<byte[]> browseFile(
@@ -454,12 +460,16 @@ private ResponseEntity<byte[]> browseDirectory(
454460
List<FileResource> resources,
455461
String namespaceName,
456462
String extensionName,
463+
String targetPlatform,
457464
String version,
458465
String path
459466
) {
460467
if(!path.isEmpty() && !path.endsWith("/")) {
461468
path += "/";
462469
}
470+
if(StringUtils.isNotEmpty(targetPlatform)) {
471+
version += "+" + targetPlatform;
472+
}
463473

464474
var urls = new HashSet<String>();
465475
var baseUrl = UrlUtil.createApiUrl(UrlUtil.getBaseUrl(), "vscode", "unpkg", namespaceName, extensionName, version);

server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import com.fasterxml.jackson.databind.ObjectMapper;
1313
import com.google.common.base.Strings;
14+
import org.apache.commons.lang3.StringUtils;
1415
import org.eclipse.openvsx.UpstreamProxyService;
1516
import org.eclipse.openvsx.UrlConfigService;
1617
import org.eclipse.openvsx.util.HttpHeadersUtil;
@@ -76,7 +77,11 @@ public ExtensionQueryResult extensionQuery(ExtensionQueryParam param, int defaul
7677
}
7778

7879
@Override
79-
public ResponseEntity<byte[]> browse(String namespaceName, String extensionName, String version, String path) {
80+
public ResponseEntity<byte[]> browse(String namespaceName, String extensionName, String version, String targetPlatform, String path) {
81+
if(StringUtils.isNotEmpty(targetPlatform)) {
82+
version += "+" + targetPlatform;
83+
}
84+
8085
var urlTemplate = urlConfigService.getUpstreamUrl() + "/vscode/unpkg/{namespace}/{extension}/{version}";
8186
var uriVariables = new HashMap<>(Map.of(
8287
"namespace", namespaceName,

server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAPI.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
********************************************************************************/
1010
package org.eclipse.openvsx.adapter;
1111

12+
import org.apache.commons.lang3.StringUtils;
1213
import org.eclipse.openvsx.util.NotFoundException;
1314
import org.eclipse.openvsx.util.TargetPlatform;
1415
import org.eclipse.openvsx.util.UrlUtil;
@@ -129,12 +130,26 @@ public ResponseEntity<byte[]> browse(
129130
HttpServletRequest request,
130131
@PathVariable String namespaceName,
131132
@PathVariable String extensionName,
132-
@PathVariable String version
133+
@PathVariable String version,
134+
@RequestParam(required = false) String target
133135
) {
136+
if(StringUtils.isEmpty(target)) {
137+
var index = version.lastIndexOf('+');
138+
if(index >= 0 && index + 1 < version.length()) {
139+
target = version.substring(index + 1);
140+
if(TargetPlatform.isValid(target)) {
141+
version = version.substring(0, index);
142+
}
143+
}
144+
}
145+
if(StringUtils.isNotEmpty(target) && !TargetPlatform.isValid(target)) {
146+
target = null;
147+
}
148+
134149
var path = UrlUtil.extractWildcardPath(request);
135150
for (var service : getVSCodeServices()) {
136151
try {
137-
return service.browse(namespaceName, extensionName, version, path);
152+
return service.browse(namespaceName, extensionName, version, target, path);
138153
} catch (NotFoundException exc) {
139154
// Try the next registry
140155
}

server/src/main/java/org/eclipse/openvsx/repositories/ExtensionVersionRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public interface ExtensionVersionRepository extends Repository<ExtensionVersion,
3434

3535
ExtensionVersion findByVersionAndTargetPlatformAndExtensionNameIgnoreCaseAndExtensionNamespaceNameIgnoreCase(String version, String targetPlatform, String extensionName, String namespace);
3636

37+
ExtensionVersion findByVersionAndTargetPlatformAndExtensionNameIgnoreCaseAndExtensionNamespaceNameIgnoreCaseAndActiveTrue(String version, String targetPlatform, String extensionName, String namespace);
38+
3739
Streamable<ExtensionVersion> findByVersionAndExtensionNameIgnoreCaseAndExtensionNamespaceNameIgnoreCase(String version, String extensionName, String namespace);
3840

3941
Streamable<ExtensionVersion> findByPublishedWithUser(UserData user);

server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,10 @@ public List<ExtensionVersion> findActiveExtensionVersionsByVersion(String versio
337337
return extensionVersionJooqRepo.findAllActiveByVersionAndExtensionNameAndNamespaceName(version, extensionName, namespaceName);
338338
}
339339

340+
public ExtensionVersion findActiveExtensionVersionByVersion(String version, String targetPlatform, String extensionName, String namespaceName) {
341+
return extensionVersionRepo.findByVersionAndTargetPlatformAndExtensionNameIgnoreCaseAndExtensionNamespaceNameIgnoreCaseAndActiveTrue(version, targetPlatform, extensionName, namespaceName);
342+
}
343+
340344
public List<ExtensionVersion> findActiveExtensionVersionsByExtensionName(String targetPlatform, String extensionName, String namespaceName) {
341345
return extensionVersionJooqRepo.findAllActiveByExtensionNameAndNamespaceName(targetPlatform, extensionName, namespaceName);
342346
}

server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,80 @@ public void testBrowseTopDir() throws Exception {
373373
.andExpect(content().json("[\"http://localhost/vscode/unpkg/foo/bar/1.3.4/extension.vsixmanifest\",\"http://localhost/vscode/unpkg/foo/bar/1.3.4/extension/\"]"));
374374
}
375375

376+
@Test
377+
public void testBrowseTopDirTargetPlatform() throws Exception {
378+
var version = "1.3.4";
379+
var targetPlatform = "linux-x64";
380+
var extensionName = "bar";
381+
var namespaceName = "foo";
382+
var namespace = new Namespace();
383+
namespace.setName(namespaceName);
384+
var extension = new Extension();
385+
extension.setId(0L);
386+
extension.setName(extensionName);
387+
extension.setNamespace(namespace);
388+
var extVersion = new ExtensionVersion();
389+
extVersion.setId(1L);
390+
extVersion.setVersion(version);
391+
extVersion.setTargetPlatform(targetPlatform);
392+
extVersion.setExtension(extension);
393+
394+
Mockito.when(repositories.findActiveExtensionVersionByVersion(version, targetPlatform, extensionName, namespaceName))
395+
.thenReturn(extVersion);
396+
397+
var vsixResource = mockFileResource(15, extVersion, "extension.vsixmanifest", RESOURCE, STORAGE_DB, "<xml></xml>".getBytes(StandardCharsets.UTF_8));
398+
var manifestResource = mockFileResource(16, extVersion, "extension/package.json", RESOURCE, STORAGE_DB, "{\"package\":\"json\"}".getBytes(StandardCharsets.UTF_8));
399+
var readmeResource = mockFileResource(17, extVersion, "extension/README.md", RESOURCE, STORAGE_DB, "README".getBytes(StandardCharsets.UTF_8));
400+
var changelogResource = mockFileResource(18, extVersion, "extension/CHANGELOG.md", RESOURCE, STORAGE_DB, "CHANGELOG".getBytes(StandardCharsets.UTF_8));
401+
var licenseResource = mockFileResource(19, extVersion, "extension/LICENSE.txt", RESOURCE, STORAGE_DB, "LICENSE".getBytes(StandardCharsets.UTF_8));
402+
var iconResource = mockFileResource(20, extVersion, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8));
403+
404+
Mockito.when(repositories.findResourceFileResources(1L, ""))
405+
.thenReturn(List.of(vsixResource, manifestResource, readmeResource, changelogResource, licenseResource, iconResource));
406+
407+
mockMvc.perform(get("/vscode/unpkg/{namespaceName}/{extensionName}/{version}?target={targetPlatform}", namespaceName, extensionName, version, targetPlatform))
408+
.andExpect(status().isOk())
409+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
410+
.andExpect(content().json("[\"http://localhost/vscode/unpkg/foo/bar/1.3.4+linux-x64/extension.vsixmanifest\",\"http://localhost/vscode/unpkg/foo/bar/1.3.4+linux-x64/extension/\"]"));
411+
}
412+
413+
@Test
414+
public void testBrowseTopDirVersionTargetPlatform() throws Exception {
415+
var version = "1.3.4-rc-1+armhf";
416+
var targetPlatform = "darwin-x64";
417+
var extensionName = "bar";
418+
var namespaceName = "foo";
419+
var namespace = new Namespace();
420+
namespace.setName(namespaceName);
421+
var extension = new Extension();
422+
extension.setId(0L);
423+
extension.setName(extensionName);
424+
extension.setNamespace(namespace);
425+
var extVersion = new ExtensionVersion();
426+
extVersion.setId(1L);
427+
extVersion.setVersion(version);
428+
extVersion.setTargetPlatform(targetPlatform);
429+
extVersion.setExtension(extension);
430+
431+
Mockito.when(repositories.findActiveExtensionVersionByVersion(version, targetPlatform, extensionName, namespaceName))
432+
.thenReturn(extVersion);
433+
434+
var vsixResource = mockFileResource(15, extVersion, "extension.vsixmanifest", RESOURCE, STORAGE_DB, "<xml></xml>".getBytes(StandardCharsets.UTF_8));
435+
var manifestResource = mockFileResource(16, extVersion, "extension/package.json", RESOURCE, STORAGE_DB, "{\"package\":\"json\"}".getBytes(StandardCharsets.UTF_8));
436+
var readmeResource = mockFileResource(17, extVersion, "extension/README.md", RESOURCE, STORAGE_DB, "README".getBytes(StandardCharsets.UTF_8));
437+
var changelogResource = mockFileResource(18, extVersion, "extension/CHANGELOG.md", RESOURCE, STORAGE_DB, "CHANGELOG".getBytes(StandardCharsets.UTF_8));
438+
var licenseResource = mockFileResource(19, extVersion, "extension/LICENSE.txt", RESOURCE, STORAGE_DB, "LICENSE".getBytes(StandardCharsets.UTF_8));
439+
var iconResource = mockFileResource(20, extVersion, "extension/images/icon128.png", RESOURCE, STORAGE_DB, "ICON128".getBytes(StandardCharsets.UTF_8));
440+
441+
Mockito.when(repositories.findResourceFileResources(1L, ""))
442+
.thenReturn(List.of(vsixResource, manifestResource, readmeResource, changelogResource, licenseResource, iconResource));
443+
444+
mockMvc.perform(get("/vscode/unpkg/{namespaceName}/{extensionName}/{version}+{targetPlatform}", namespaceName, extensionName, version, targetPlatform))
445+
.andExpect(status().isOk())
446+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
447+
.andExpect(content().json("[\"http://localhost/vscode/unpkg/foo/bar/1.3.4-rc-1+armhf+darwin-x64/extension.vsixmanifest\",\"http://localhost/vscode/unpkg/foo/bar/1.3.4-rc-1+armhf+darwin-x64/extension/\"]"));
448+
}
449+
376450
@Test
377451
public void testBrowseVsixManifest() throws Exception {
378452
var version = "1.3.4";
@@ -601,6 +675,95 @@ public void testBrowseIcon() throws Exception {
601675
.andExpect(content().bytes(content));
602676
}
603677

678+
@Test
679+
public void testBrowseIconTargetPlatform() throws Exception {
680+
var version = "1.3.4";
681+
var targetPlatform = "win32-x64";
682+
var extensionName = "bar";
683+
var namespaceName = "foo";
684+
var namespace = new Namespace();
685+
namespace.setName(namespaceName);
686+
var extension = new Extension();
687+
extension.setId(0L);
688+
extension.setName(extensionName);
689+
extension.setNamespace(namespace);
690+
var extVersion = new ExtensionVersion();
691+
extVersion.setId(1L);
692+
extVersion.setVersion(version);
693+
extVersion.setTargetPlatform(targetPlatform);
694+
extVersion.setExtension(extension);
695+
Mockito.when(repositories.findActiveExtensionVersionByVersion(version, targetPlatform, extensionName, namespaceName))
696+
.thenReturn(extVersion);
697+
698+
var content = "ICON128".getBytes(StandardCharsets.UTF_8);
699+
var iconResource = mockFileResource(20, extVersion, "extension/images/icon128.png", RESOURCE, STORAGE_DB, content);
700+
Mockito.when(repositories.findResourceFileResources(1L, "extension/images/icon128.png"))
701+
.thenReturn(List.of(iconResource));
702+
703+
mockMvc.perform(get("/vscode/unpkg/{namespaceName}/{extensionName}/{version}/{path}?target={targetPlatform}", namespaceName, extensionName, version, "extension/images/icon128.png", targetPlatform))
704+
.andExpect(status().isOk())
705+
.andExpect(content().bytes(content));
706+
}
707+
708+
@Test
709+
public void testBrowseIconVersionTargetPlatform() throws Exception {
710+
var version = "1.3.4-ga+armhf";
711+
var targetPlatform = "alpine-x64";
712+
var extensionName = "bar";
713+
var namespaceName = "foo";
714+
var namespace = new Namespace();
715+
namespace.setName(namespaceName);
716+
var extension = new Extension();
717+
extension.setId(0L);
718+
extension.setName(extensionName);
719+
extension.setNamespace(namespace);
720+
var extVersion = new ExtensionVersion();
721+
extVersion.setId(1L);
722+
extVersion.setVersion(version);
723+
extVersion.setTargetPlatform(targetPlatform);
724+
extVersion.setExtension(extension);
725+
Mockito.when(repositories.findActiveExtensionVersionByVersion(version, targetPlatform, extensionName, namespaceName))
726+
.thenReturn(extVersion);
727+
728+
var content = "ICON128".getBytes(StandardCharsets.UTF_8);
729+
var iconResource = mockFileResource(20, extVersion, "extension/images/icon128.png", RESOURCE, STORAGE_DB, content);
730+
Mockito.when(repositories.findResourceFileResources(1L, "extension/images/icon128.png"))
731+
.thenReturn(List.of(iconResource));
732+
733+
mockMvc.perform(get("/vscode/unpkg/{namespaceName}/{extensionName}/{version}+{targetPlatform}/{path}", namespaceName, extensionName, version, targetPlatform, "extension/images/icon128.png"))
734+
.andExpect(status().isOk())
735+
.andExpect(content().bytes(content));
736+
}
737+
738+
@Test
739+
public void testBrowseIconVersionInvalidTargetPlatform() throws Exception {
740+
var version = "1.3.4-ga+armhf";
741+
var extensionName = "bar";
742+
var namespaceName = "foo";
743+
var namespace = new Namespace();
744+
namespace.setName(namespaceName);
745+
var extension = new Extension();
746+
extension.setId(0L);
747+
extension.setName(extensionName);
748+
extension.setNamespace(namespace);
749+
var extVersion = new ExtensionVersion();
750+
extVersion.setId(1L);
751+
extVersion.setVersion(version);
752+
extVersion.setTargetPlatform(TargetPlatform.NAME_UNIVERSAL);
753+
extVersion.setExtension(extension);
754+
Mockito.when(repositories.findActiveExtensionVersionsByVersion(version, extensionName, namespaceName))
755+
.thenReturn(List.of(extVersion));
756+
757+
var content = "ICON128".getBytes(StandardCharsets.UTF_8);
758+
var iconResource = mockFileResource(20, extVersion, "extension/images/icon128.png", RESOURCE, STORAGE_DB, content);
759+
Mockito.when(repositories.findResourceFileResources(1L, "extension/images/icon128.png"))
760+
.thenReturn(List.of(iconResource));
761+
762+
mockMvc.perform(get("/vscode/unpkg/{namespaceName}/{extensionName}/{version}/{path}", namespaceName, extensionName, version, "extension/images/icon128.png"))
763+
.andExpect(status().isOk())
764+
.andExpect(content().bytes(content));
765+
}
766+
604767
@Test
605768
public void testDownload() throws Exception {
606769
mockExtensionVersion();

server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ void testExecuteQueries() {
156156
() -> repositories.topNamespaceExtensionVersions(NOW, 1),
157157
() -> repositories.findFileResourcesByExtensionVersionIdAndType(LONG_LIST, STRING_LIST),
158158
() -> repositories.findActiveExtensionVersionsByVersion("version", "extensionName", "namespaceName"),
159+
() -> repositories.findActiveExtensionVersionByVersion("version", "targetPlatform", "extensionName", "namespaceName"),
159160
() -> repositories.findResourceFileResources(1L, "prefix"),
160161
() -> repositories.findActiveExtensionVersions(LONG_LIST, "targetPlatform"),
161162
() -> repositories.findActiveExtension("name", "namespaceName"),

0 commit comments

Comments
 (0)