Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public record Extension(
@Schema(description = "Date and time when this extension was last updated (ISO-8601)")
String lastUpdated,
List<String> categories,
@Schema(description = "Flag extension as preview")
@Schema(description = "Flag extension as preview or deprecated (e.g., 'preview,deprecated', 'preview', 'deprecated')")
String flags
) {
public static final String FLAG_PREVIEW = "preview";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,4 +548,46 @@ private boolean isWebExtension(ExtensionVersion extVer) {
private boolean test(int flags, int flag) {
return (flags & flag) != 0;
}

/**
* Build a VS Code Extensions Control Manifest (partial) containing only the `deprecated` dictionary.
* The dictionary maps extension id (publisher.name) to either `true` or an object with optional fields
* such as `disallowInstall` and `extension` (replacement recommendation), per IRawExtensionsControlManifest.
*/
public Map<String, Object> buildExtensionsControlManifestDeprecated() {
var deprecatedMap = new LinkedHashMap<String, Object>();

for (var ext : repositories.findAllActiveExtensions()) {
if (!ext.isDeprecated()) {
continue;
}

var extensionId = ext.getNamespace().getName() + "." + ext.getName();

var details = new LinkedHashMap<String, Object>();

if (ext.getDownloadable() != null && !ext.getDownloadable()) {
details.put("disallowInstall", true);
}

var replacement = ext.getReplacement();
if (replacement != null) {
var latestReplacement = repositories.findLatestVersion(replacement, null, false, true);
var replacementDisplayName = latestReplacement != null ? latestReplacement.getDisplayName() : replacement.getName();
var replacementId = replacement.getNamespace().getName() + "." + replacement.getName();
details.put("extension", Map.of(
"id", replacementId,
"displayName", replacementDisplayName
));
}

if (details.isEmpty()) {
deprecatedMap.put(extensionId, Boolean.TRUE);
} else {
deprecatedMap.put(extensionId, details);
}
}

return deprecatedMap;
}
}
16 changes: 16 additions & 0 deletions server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.eclipse.openvsx.adapter.ExtensionQueryParam.*;
import static org.eclipse.openvsx.adapter.ExtensionQueryResult.ExtensionFile.*;
Expand Down Expand Up @@ -88,6 +89,21 @@ public ExtensionQueryResult extensionQuery(@RequestBody @Parameter(description =
return extensionQueryRequestHandler.getResult(param, size, DEFAULT_PAGE_SIZE);
}

@GetMapping(
path = "/vscode/gallery/extensioncontrol",
produces = MediaType.APPLICATION_JSON_VALUE
)
@CrossOrigin
@Operation(summary = "Provides VS Code Extensions Control manifest (partial) containing 'deprecated' mapping")
@ApiResponse(
responseCode = "200",
description = "Returns the control manifest"
)
public Map<String, Object> getExtensionsControlManifest() {
// Only provide the deprecated dictionary for now
return Map.of("deprecated", local.buildExtensionsControlManifestDeprecated());
}

@Observed
@GetMapping("/vscode/asset/{namespaceName}/{extensionName}/{version}/{assetType}/**")
@CrossOrigin
Expand Down