提交 82944759 编写于 作者: J Joao Moreno

Merge branch 'marketplace-cdn'

......@@ -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 {
......
......@@ -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';
......@@ -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 {
......@@ -39,6 +38,7 @@ interface IRawGalleryExtensionVersion {
version: string;
lastUpdated: string;
assetUri: string;
fallbackAssetUri: string;
files: IRawGalleryExtensionFile[];
properties?: IRawGalleryExtensionProperty[];
}
......@@ -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[] {
......@@ -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 {
......@@ -365,13 +371,12 @@ 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);
......@@ -379,20 +384,12 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
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);
}
......@@ -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)
......@@ -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;
});
......@@ -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);
});
......@@ -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;
......
......@@ -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 {
......@@ -122,11 +122,11 @@ 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 {
......@@ -134,7 +134,7 @@ class Extension implements IExtension {
}
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 {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册