adopt extension resource url to gallery manifest (#244105)

This commit is contained in:
Sandeep Somavarapu 2025-03-20 12:16:56 +01:00 committed by GitHub
parent 5fa41d4575
commit ea8c62369e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 79 additions and 41 deletions

View File

@ -14,6 +14,7 @@ export const enum ExtensionGalleryResourceType {
PublisherViewUri = 'PublisherViewUriTemplate',
ExtensionDetailsViewUri = 'ExtensionDetailsViewUriTemplate',
ExtensionRatingViewUri = 'ExtensionRatingViewUriTemplate',
ExtensionResourceUri = 'ExtensionResourceUriTemplate',
}
export const enum Flag {
@ -68,4 +69,18 @@ export interface IExtensionGalleryManifestService {
getExtensionGalleryManifest(): Promise<IExtensionGalleryManifest | null>;
}
export function getExtensionGalleryManifestResourceUri(manifest: IExtensionGalleryManifest, type: ExtensionGalleryResourceType, version?: string): string | undefined {
for (const resource of manifest.resources) {
const [r, v] = resource.type.split('/');
if (r !== type) {
continue;
}
if (!version || v === version) {
return resource.id;
}
break;
}
return undefined;
}
export const ExtensionGalleryServiceUrlConfigKey = 'extensions.gallery.serviceUrl';

View File

@ -77,6 +77,13 @@ export class ExtensionGalleryManifestService extends Disposable implements IExte
});
}
if (extensionsGallery.resourceUrlTemplate) {
resources.push({
id: extensionsGallery.resourceUrlTemplate,
type: ExtensionGalleryResourceType.ExtensionResourceUri
});
}
const filtering = [
{
name: FilterType.Tag,

View File

@ -29,7 +29,7 @@ import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { StopWatch } from '../../../base/common/stopwatch.js';
import { format2 } from '../../../base/common/strings.js';
import { IAssignmentService } from '../../assignment/common/assignment.js';
import { ExtensionGalleryResourceType, Flag, IExtensionGalleryManifest, IExtensionGalleryManifestService } from './extensionGalleryManifest.js';
import { ExtensionGalleryResourceType, Flag, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from './extensionGalleryManifest.js';
const CURRENT_TARGET_PLATFORM = isWeb ? TargetPlatform.WEB : getTargetPlatform(platform, arch);
const SEARCH_ACTIVITY_HEADER_NAME = 'X-Market-Search-Activity-Id';
@ -441,20 +441,6 @@ function setTelemetry(extension: IGalleryExtension, index: number, querySource?:
extension.telemetryData = { index, querySource, queryActivityId: extension.queryContext?.[SEARCH_ACTIVITY_HEADER_NAME] };
}
function getExtensionGalleryManifestResourceUri(manifest: IExtensionGalleryManifest, type: ExtensionGalleryResourceType, version?: string): string | undefined {
for (const resource of manifest.resources) {
const [r, v] = resource.type.split('/');
if (r !== type) {
continue;
}
if (!version || v === version) {
return resource.id;
}
break;
}
return undefined;
}
function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, allTargetPlatforms: TargetPlatform[], extensionGalleryManifest: IExtensionGalleryManifest, queryContext?: IStringDictionary<any>): IGalleryExtension {
const latestVersion = galleryExtension.versions[0];
const assets: IGalleryExtensionAssets = {

View File

@ -13,6 +13,7 @@ import { IEnvironmentService } from '../../environment/common/environment.js';
import { ILogService } from '../../log/common/log.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
import { AbstractExtensionResourceLoaderService, IExtensionResourceLoaderService } from '../common/extensionResourceLoader.js';
import { IExtensionGalleryManifestService } from '../../extensionManagement/common/extensionGalleryManifest.js';
class ExtensionResourceLoaderService extends AbstractExtensionResourceLoaderService {
@ -24,9 +25,10 @@ class ExtensionResourceLoaderService extends AbstractExtensionResourceLoaderServ
@IProductService productService: IProductService,
@IEnvironmentService environmentService: IEnvironmentService,
@IConfigurationService configurationService: IConfigurationService,
@ILogService private readonly _logService: ILogService,
@IExtensionGalleryManifestService extensionGalleryManifestService: IExtensionGalleryManifestService,
@ILogService logService: ILogService,
) {
super(fileService, storageService, productService, environmentService, configurationService);
super(fileService, storageService, productService, environmentService, configurationService, extensionGalleryManifestService, logService);
}
async readExtensionResource(uri: URI): Promise<string> {
@ -38,7 +40,7 @@ class ExtensionResourceLoaderService extends AbstractExtensionResourceLoaderServ
}
const requestInit: RequestInit = {};
if (this.isExtensionGalleryResource(uri)) {
if (await this.isExtensionGalleryResource(uri)) {
requestInit.headers = await this.getExtensionGalleryRequestHeaders();
requestInit.mode = 'cors'; /* set mode to cors so that above headers are always passed */
}

View File

@ -17,6 +17,9 @@ import { TelemetryLevel } from '../../telemetry/common/telemetry.js';
import { getTelemetryLevel, supportsTelemetry } from '../../telemetry/common/telemetryUtils.js';
import { RemoteAuthorities } from '../../../base/common/network.js';
import { TargetPlatform } from '../../extensions/common/extensions.js';
import { ExtensionGalleryResourceType, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../extensionManagement/common/extensionGalleryManifest.js';
import { ILogService } from '../../log/common/log.js';
import { Disposable } from '../../../base/common/lifecycle.js';
const WEB_EXTENSION_RESOURCE_END_POINT_SEGMENT = '/web-extension-resource/';
@ -36,17 +39,17 @@ export interface IExtensionResourceLoaderService {
/**
* Returns whether the gallery provides extension resources.
*/
readonly supportsExtensionGalleryResources: boolean;
supportsExtensionGalleryResources(): Promise<boolean>;
/**
* Return true if the given URI is a extension gallery resource.
*/
isExtensionGalleryResource(uri: URI): boolean;
isExtensionGalleryResource(uri: URI): Promise<boolean>;
/**
* Computes the URL of a extension gallery resource. Returns `undefined` if gallery does not provide extension resources.
*/
getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string; targetPlatform?: TargetPlatform }, path?: string): URI | undefined;
getExtensionGalleryResourceURL(galleryExtension: { publisher: string; name: string; version: string; targetPlatform?: TargetPlatform }, path?: string): Promise<URI | undefined>;
}
export function migratePlatformSpecificExtensionGalleryResourceURL(resource: URI, targetPlatform: TargetPlatform): URI | undefined {
@ -61,12 +64,14 @@ export function migratePlatformSpecificExtensionGalleryResourceURL(resource: URI
return resource.with({ query: null, path: paths.join('/') });
}
export abstract class AbstractExtensionResourceLoaderService implements IExtensionResourceLoaderService {
export abstract class AbstractExtensionResourceLoaderService extends Disposable implements IExtensionResourceLoaderService {
readonly _serviceBrand: undefined;
private readonly _extensionGalleryResourceUrlTemplate: string | undefined;
private readonly _extensionGalleryAuthority: string | undefined;
private readonly _initPromise: Promise<void>;
private _extensionGalleryResourceUrlTemplate: string | undefined;
private _extensionGalleryAuthority: string | undefined;
constructor(
protected readonly _fileService: IFileService,
@ -74,18 +79,35 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi
private readonly _productService: IProductService,
private readonly _environmentService: IEnvironmentService,
private readonly _configurationService: IConfigurationService,
private readonly _extensionGalleryManifestService: IExtensionGalleryManifestService,
protected readonly _logService: ILogService,
) {
if (_productService.extensionsGallery) {
this._extensionGalleryResourceUrlTemplate = _productService.extensionsGallery.resourceUrlTemplate;
this._extensionGalleryAuthority = this._extensionGalleryResourceUrlTemplate ? this._getExtensionGalleryAuthority(URI.parse(this._extensionGalleryResourceUrlTemplate)) : undefined;
super();
this._initPromise = this._init();
}
private async _init(): Promise<void> {
try {
const manifest = await this._extensionGalleryManifestService.getExtensionGalleryManifest();
this.resolve(manifest);
this._register(this._extensionGalleryManifestService.onDidChangeExtensionGalleryManifest(() => this.resolve(manifest)));
} catch (error) {
this._logService.error(error);
}
}
public get supportsExtensionGalleryResources(): boolean {
private resolve(manifest: IExtensionGalleryManifest | null): void {
this._extensionGalleryResourceUrlTemplate = manifest ? getExtensionGalleryManifestResourceUri(manifest, ExtensionGalleryResourceType.ExtensionResourceUri) : undefined;
this._extensionGalleryAuthority = this._extensionGalleryResourceUrlTemplate ? this._getExtensionGalleryAuthority(URI.parse(this._extensionGalleryResourceUrlTemplate)) : undefined;
}
public async supportsExtensionGalleryResources(): Promise<boolean> {
await this._initPromise;
return this._extensionGalleryResourceUrlTemplate !== undefined;
}
public getExtensionGalleryResourceURL({ publisher, name, version, targetPlatform }: { publisher: string; name: string; version: string; targetPlatform?: TargetPlatform }, path?: string): URI | undefined {
public async getExtensionGalleryResourceURL({ publisher, name, version, targetPlatform }: { publisher: string; name: string; version: string; targetPlatform?: TargetPlatform }, path?: string): Promise<URI | undefined> {
await this._initPromise;
if (this._extensionGalleryResourceUrlTemplate) {
const uri = URI.parse(format2(this._extensionGalleryResourceUrlTemplate, {
publisher,
@ -105,7 +127,8 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi
public abstract readExtensionResource(uri: URI): Promise<string>;
isExtensionGalleryResource(uri: URI): boolean {
async isExtensionGalleryResource(uri: URI): Promise<boolean> {
await this._initPromise;
return !!this._extensionGalleryAuthority && this._extensionGalleryAuthority === this._getExtensionGalleryAuthority(uri);
}

View File

@ -13,6 +13,8 @@ import { IEnvironmentService } from '../../environment/common/environment.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
import { CancellationToken } from '../../../base/common/cancellation.js';
import { AbstractExtensionResourceLoaderService, IExtensionResourceLoaderService } from './extensionResourceLoader.js';
import { IExtensionGalleryManifestService } from '../../extensionManagement/common/extensionGalleryManifest.js';
import { ILogService } from '../../log/common/log.js';
export class ExtensionResourceLoaderService extends AbstractExtensionResourceLoaderService {
@ -22,13 +24,15 @@ export class ExtensionResourceLoaderService extends AbstractExtensionResourceLoa
@IProductService productService: IProductService,
@IEnvironmentService environmentService: IEnvironmentService,
@IConfigurationService configurationService: IConfigurationService,
@IExtensionGalleryManifestService extensionGalleryManifestService: IExtensionGalleryManifestService,
@IRequestService private readonly _requestService: IRequestService,
@ILogService logService: ILogService,
) {
super(fileService, storageService, productService, environmentService, configurationService);
super(fileService, storageService, productService, environmentService, configurationService, extensionGalleryManifestService, logService);
}
async readExtensionResource(uri: URI): Promise<string> {
if (this.isExtensionGalleryResource(uri)) {
if (await this.isExtensionGalleryResource(uri)) {
const headers = await this.getExtensionGalleryRequestHeaders();
const requestContext = await this._requestService.request({ url: uri.toString(), headers }, CancellationToken.None);
return (await asTextOrError(requestContext)) || '';

View File

@ -56,7 +56,7 @@ export class WebLanguagePacksService extends LanguagePackBaseService {
}
// get the resource uri and return it
const uri = this.extensionResourceLoaderService.getExtensionGalleryResourceURL({
const uri = await this.extensionResourceLoaderService.getExtensionGalleryResourceURL({
// If translation is defined then manifest should have been defined.
name: manifest!.name,
publisher: manifest!.publisher,

View File

@ -306,7 +306,7 @@ class InstalledThemesPicker {
let marketplaceThemePicker: MarketplaceThemesPicker | undefined;
if (this.extensionGalleryService.isEnabled()) {
if (this.extensionResourceLoaderService.supportsExtensionGalleryResources && this.options.browseMessage) {
if (await this.extensionResourceLoaderService.supportsExtensionGalleryResources() && this.options.browseMessage) {
marketplaceThemePicker = this.instantiationService.createInstance(MarketplaceThemesPicker, this.getMarketplaceColorThemes.bind(this), this.options.marketplaceTag);
picks = [configurationEntry(this.options.browseMessage, ConfigureItem.BROWSE_GALLERY), ...picks];
} else {
@ -772,7 +772,7 @@ registerAction2(class extends Action2 {
const extensionResourceLoaderService = accessor.get(IExtensionResourceLoaderService);
const instantiationService = accessor.get(IInstantiationService);
if (!extensionGalleryService.isEnabled() || !extensionResourceLoaderService.supportsExtensionGalleryResources) {
if (!extensionGalleryService.isEnabled() || !await extensionResourceLoaderService.supportsExtensionGalleryResources()) {
return;
}
const currentTheme = themeService.getColorTheme();

View File

@ -146,7 +146,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
}
} else if (isUriComponents(e)) {
const extensionLocation = URI.revive(e);
if (this.extensionResourceLoaderService.isExtensionGalleryResource(extensionLocation)) {
if (await this.extensionResourceLoaderService.isExtensionGalleryResource(extensionLocation)) {
extensionGalleryResources.push(extensionLocation);
} else {
extensionLocations.push(extensionLocation);
@ -651,7 +651,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
}
private async toWebExtensionFromGallery(galleryExtension: IGalleryExtension, metadata?: Metadata): Promise<IWebExtension> {
const extensionLocation = this.extensionResourceLoaderService.getExtensionGalleryResourceURL({
const extensionLocation = await this.extensionResourceLoaderService.getExtensionGalleryResourceURL({
publisher: galleryExtension.publisher,
name: galleryExtension.name,
version: galleryExtension.version,

View File

@ -375,7 +375,7 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme
}
public async getMarketplaceColorThemes(publisher: string, name: string, version: string): Promise<IWorkbenchColorTheme[]> {
const extensionLocation = this.extensionResourceLoaderService.getExtensionGalleryResourceURL({ publisher, name, version }, 'extension');
const extensionLocation = await this.extensionResourceLoaderService.getExtensionGalleryResourceURL({ publisher, name, version }, 'extension');
if (extensionLocation) {
try {
const manifestContent = await this.extensionResourceLoaderService.readExtensionResource(resources.joinPath(extensionLocation, 'package.json'));
@ -599,7 +599,7 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme
}
public async getMarketplaceFileIconThemes(publisher: string, name: string, version: string): Promise<IWorkbenchFileIconTheme[]> {
const extensionLocation = this.extensionResourceLoaderService.getExtensionGalleryResourceURL({ publisher, name, version }, 'extension');
const extensionLocation = await this.extensionResourceLoaderService.getExtensionGalleryResourceURL({ publisher, name, version }, 'extension');
if (extensionLocation) {
try {
const manifestContent = await this.extensionResourceLoaderService.readExtensionResource(resources.joinPath(extensionLocation, 'package.json'));
@ -705,7 +705,7 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme
}
public async getMarketplaceProductIconThemes(publisher: string, name: string, version: string): Promise<IWorkbenchProductIconTheme[]> {
const extensionLocation = this.extensionResourceLoaderService.getExtensionGalleryResourceURL({ publisher, name, version }, 'extension');
const extensionLocation = await this.extensionResourceLoaderService.getExtensionGalleryResourceURL({ publisher, name, version }, 'extension');
if (extensionLocation) {
try {
const manifestContent = await this.extensionResourceLoaderService.readExtensionResource(resources.joinPath(extensionLocation, 'package.json'));

View File

@ -21,6 +21,7 @@ import { IStorageService } from '../../../../../platform/storage/common/storage.
import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js';
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { ExtensionGalleryManifestService } from '../../../../../platform/extensionManagement/common/extensionGalleryManifestService.js';
const undefinedStyle = { bold: undefined, underline: undefined, italic: undefined };
const unsetStyle = { bold: false, underline: false, italic: false };
@ -89,7 +90,7 @@ suite('Themes - TokenStyleResolving', () => {
const environmentService = new (mock<IEnvironmentService>())();
const configurationService = new (mock<IConfigurationService>())();
const extensionResourceLoaderService = new ExtensionResourceLoaderService(fileService, storageService, TestProductService, environmentService, configurationService, requestService);
const extensionResourceLoaderService = new ExtensionResourceLoaderService(fileService, storageService, TestProductService, environmentService, configurationService, new ExtensionGalleryManifestService(TestProductService), requestService, new NullLogService());
const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService());
fileService.registerProvider(Schemas.file, diskFileSystemProvider);