Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt latest Marketplace CDN changes #16275

Merged
merged 4 commits into from
Nov 30, 2016
Merged
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
18 changes: 11 additions & 7 deletions src/vs/platform/extensionManagement/common/extensionManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,18 @@ export interface IGalleryExtensionProperties {
engine?: string;
}

export interface IGalleryExtensionAsset {
uri: string;
fallbackUri: string;
}

export interface IGalleryExtensionAssets {
manifest: string;
readme: string;
changelog: string;
download: string;
icon: string;
iconFallback: string;
license: string;
manifest: IGalleryExtensionAsset;
readme: IGalleryExtensionAsset;
changelog: IGalleryExtensionAsset;
download: IGalleryExtensionAsset;
icon: IGalleryExtensionAsset;
license: IGalleryExtensionAsset;
}

export interface IGalleryExtension {
Expand Down
102 changes: 48 additions & 54 deletions src/vs/platform/extensionManagement/node/extensionGalleryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { distinct } from 'vs/base/common/arrays';
import { getErrorMessage } from 'vs/base/common/errors';
import { memoize } from 'vs/base/common/decorators';
import { ArraySet } from 'vs/base/common/set';
import { IGalleryExtension, IExtensionGalleryService, IQueryOptions, SortBy, SortOrder, IExtensionManifest, EXTENSION_IDENTIFIER_REGEX } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest, EXTENSION_IDENTIFIER_REGEX } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionTelemetry';
import { assign, getOrDefault } from 'vs/base/common/objects';
import { IRequestService } from 'vs/platform/request/node/request';
Expand All @@ -22,7 +22,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import pkg from 'vs/platform/package';
import product from 'vs/platform/product';
import { isVersionValid } from 'vs/platform/extensions/node/extensionValidator';
import * as url from 'url';
import { getCommonHTTPHeaders } from 'vs/platform/environment/node/http';

interface IRawGalleryExtensionFile {
Expand All @@ -39,6 +38,7 @@ interface IRawGalleryExtensionVersion {
version: string;
lastUpdated: string;
assetUri: string;
fallbackAssetUri: string;
files: IRawGalleryExtensionFile[];
properties?: IRawGalleryExtensionProperty[];
}
Expand Down Expand Up @@ -193,9 +193,29 @@ function getStatistic(statistics: IRawGalleryExtensionStatistics[], name: string
return result ? result.value : 0;
}

function getAssetSource(files: IRawGalleryExtensionFile[], type: string): string {
const result = files.filter(f => f.assetType === type)[0];
return result && result.source;
function getVersionAsset(version: IRawGalleryExtensionVersion, type: string): IGalleryExtensionAsset {
const result = version.files.filter(f => f.assetType === type)[0];

if (!result) {
if (type === AssetType.Icon) {
const uri = require.toUrl('./media/defaultIcon.png');
return { uri, fallbackUri: uri };
}

return null;
}

if (type === AssetType.VSIX) {
return {
uri: `${version.fallbackAssetUri}/${type}?redirect=true&install=true`,
fallbackUri: `${version.fallbackAssetUri}/${type}?install=true`
};
}

return {
uri: `${version.assetUri}/${type}`,
fallbackUri: `${version.fallbackAssetUri}/${type}`
};
}

function getDependencies(version: IRawGalleryExtensionVersion): string[] {
Expand All @@ -211,27 +231,13 @@ function getEngine(version: IRawGalleryExtensionVersion): string {

function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string): IGalleryExtension {
const [version] = galleryExtension.versions;

let iconFallback = getAssetSource(version.files, AssetType.Icon);
let icon: string;

if (iconFallback) {
const parsedUrl = url.parse(iconFallback, true);
parsedUrl.search = undefined;
parsedUrl.query['redirect'] = 'true';
icon = url.format(parsedUrl);
} else {
iconFallback = icon = require.toUrl('./media/defaultIcon.png');
}

const assets = {
manifest: getAssetSource(version.files, AssetType.Manifest),
readme: getAssetSource(version.files, AssetType.Details),
changelog: getAssetSource(version.files, AssetType.Changelog),
download: `${getAssetSource(version.files, AssetType.VSIX)}?install=true`,
icon,
iconFallback,
license: getAssetSource(version.files, AssetType.License)
manifest: getVersionAsset(version, AssetType.Manifest),
readme: getVersionAsset(version, AssetType.Details),
changelog: getVersionAsset(version, AssetType.Changelog),
download: getVersionAsset(version, AssetType.VSIX),
icon: getVersionAsset(version, AssetType.Icon),
license: getVersionAsset(version, AssetType.License)
};

return {
Expand Down Expand Up @@ -365,34 +371,25 @@ export class ExtensionGalleryService implements IExtensionGalleryService {

download(extension: IGalleryExtension): TPromise<string> {
return this.loadCompatibleVersion(extension).then(extension => {
const url = extension.assets.download;
const zipPath = path.join(tmpdir(), extension.id);
const data = getGalleryExtensionTelemetryData(extension);
const startTime = new Date().getTime();
const log = duration => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration }));

return this.getAsset({ url })
return this.getAsset(extension.assets.download)
.then(context => download(zipPath, context))
.then(() => log(new Date().getTime() - startTime))
.then(() => zipPath);
});
}

getReadme(extension: IGalleryExtension): TPromise<string> {
const url = extension.assets.readme;

if (!url) {
return TPromise.wrapError('not available');
}

return this.getAsset({ url })
return this.getAsset(extension.assets.readme)
.then(asText);
}

getManifest(extension: IGalleryExtension): TPromise<IExtensionManifest> {
const url = extension.assets.manifest;

return this.getAsset({ url })
return this.getAsset(extension.assets.manifest)
.then(asText)
.then(JSON.parse);
}
Expand All @@ -406,6 +403,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
if (extension.properties.engine && this.isEngineValid(extension.properties.engine)) {
return TPromise.wrap(extension);
}

const query = new Query()
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, 1)
Expand All @@ -416,14 +414,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {

return this.queryGallery(query).then(({ galleryExtensions }) => {
const [rawExtension] = galleryExtensions;

if (!rawExtension) {
return TPromise.wrapError(new Error(localize('notFound', "Extension not found")));
}

return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions)
.then(rawVersion => {
extension.properties.dependencies = getDependencies(rawVersion);
extension.properties.engine = getEngine(rawVersion);
extension.assets.download = `${getAssetSource(rawVersion.files, AssetType.VSIX)}?install=true`;
extension.assets.download = getVersionAsset(rawVersion, AssetType.VSIX);
extension.version = rawVersion.version;
return extension;
});
Expand Down Expand Up @@ -480,29 +480,23 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
});
}

/**
* Always try with the `redirect=true` query string.
* If that does not return 200, try without it.
*/
private getAsset(options: IRequestOptions): TPromise<IRequestContext> {
const parsedUrl = url.parse(options.url, true);
parsedUrl.search = undefined;
parsedUrl.query['redirect'] = 'true';
private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}): TPromise<IRequestContext> {
const baseOptions = { type: 'GET' };

return this.commonHTTPHeaders.then(headers => {
headers = assign({}, headers, options.headers || {});
options = assign({}, options, { headers });
options = assign({}, options, baseOptions, { headers });

const cdnUrl = url.format(parsedUrl);
const cdnOptions = assign({}, options, { url: cdnUrl });
const firstOptions = assign({}, options, { url: asset.uri });

return this.requestService.request(cdnOptions)
return this.requestService.request(firstOptions)
.then(context => context.res.statusCode === 200 ? context : TPromise.wrapError('expected 200'))
.then(null, err => {
this.telemetryService.publicLog('galleryService:requestError', { cdn: true, message: getErrorMessage(err) });
this.telemetryService.publicLog('galleryService:cdnFallback', { url: cdnUrl });
this.telemetryService.publicLog('galleryService:cdnFallback', { url: asset.uri });

return this.requestService.request(options).then(null, err => {
const fallbackOptions = assign({}, options, { url: asset.fallbackUri });
return this.requestService.request(fallbackOptions).then(null, err => {
this.telemetryService.publicLog('galleryService:requestError', { cdn: false, message: getErrorMessage(err) });
return TPromise.wrapError(err);
});
Expand Down Expand Up @@ -537,10 +531,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}

const version = versions[0];
const url = getAssetSource(version.files, AssetType.Manifest);
const asset = getVersionAsset(version, AssetType.Manifest);
const headers = { 'Accept-Encoding': 'gzip' };

return this.getAsset({ url, headers })
return this.getAsset(asset, { headers })
.then(context => asJson<IExtensionManifest>(context))
.then(manifest => {
const engine = manifest.engines.vscode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class Extension implements IExtension {
return this.local.changelogUrl;
}

return this.gallery && this.gallery.assets.changelog;
return this.gallery && this.gallery.assets.changelog && this.gallery.assets.changelog.uri;
}

get iconUrl(): string {
Expand All @@ -122,19 +122,19 @@ class Extension implements IExtension {
}

private get galleryIconUrl(): string {
return this.gallery && this.gallery.assets.icon;
return this.gallery && this.gallery.assets.icon.uri;
}

private get galleryIconUrlFallback(): string {
return this.gallery && this.gallery.assets.iconFallback;
return this.gallery && this.gallery.assets.icon.fallbackUri;
}

private get defaultIconUrl(): string {
return require.toUrl('../browser/media/defaultIcon.png');
}

get licenseUrl(): string {
return this.gallery && this.gallery.assets.license;
return this.gallery && this.gallery.assets.license && this.gallery.assets.license.uri;
}

get state(): ExtensionState {
Expand Down