Skip to content

Commit c8caac3

Browse files
committed
contain gallery code inside GalleryService
related to microsoft#10180
1 parent 416fe53 commit c8caac3

8 files changed

Lines changed: 185 additions & 192 deletions

File tree

src/vs/code/node/cliProcessMain.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ class Main {
113113
console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id));
114114
console.log(localize('installing', "Installing..."));
115115

116-
return this.extensionManagementService.install(extension).then(() => {
117-
console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.versions[0].version));
118-
});
116+
return this.extensionManagementService.installFromGallery(extension)
117+
.then(() => console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version)));
119118
});
120119
});
121120
});

src/vs/code/node/sharedProcessMain.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ
1717
import { IEventService } from 'vs/platform/event/common/event';
1818
import { EventService } from 'vs/platform/event/common/eventService';
1919
import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
20-
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
20+
import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
2121
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
22+
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
2223
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2324
import { NodeConfigurationService } from 'vs/platform/configuration/node/nodeConfigurationService';
2425
import { ITelemetryService, combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -55,7 +56,6 @@ function main(server: Server): void {
5556

5657
services.set(IEventService, new SyncDescriptor(EventService));
5758
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService));
58-
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
5959
services.set(IConfigurationService, new SyncDescriptor(NodeConfigurationService));
6060

6161
const instantiationService = new InstantiationService(services);
@@ -93,6 +93,9 @@ function main(server: Server): void {
9393
services.set(ITelemetryService, NullTelemetryService);
9494
}
9595

96+
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
97+
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
98+
9699
const instantiationService2 = instantiationService.createChild(services);
97100

98101
instantiationService2.invokeFunction(accessor => {

src/vs/platform/extensionManagement/common/extensionManagement.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,24 @@ export interface IExtensionManifest {
2222
icon?: string;
2323
}
2424

25-
export interface IGalleryVersion {
26-
version: string;
27-
date: string;
28-
manifestUrl: string;
29-
readmeUrl: string;
30-
downloadUrl: string;
31-
iconUrl: string;
32-
licenseUrl: string;
33-
downloadHeaders: { [key: string]: string; };
34-
}
35-
3625
export interface IExtensionIdentity {
3726
name: string;
3827
publisher: string;
3928
}
4029

30+
export interface IGalleryExtensionAssets {
31+
manifest: string;
32+
readme: string;
33+
download: string;
34+
icon: string;
35+
license: string;
36+
}
37+
4138
export interface IGalleryExtension {
4239
id: string;
4340
name: string;
41+
version: string;
42+
date: string;
4443
displayName: string;
4544
publisherId: string;
4645
publisher: string;
@@ -49,7 +48,8 @@ export interface IGalleryExtension {
4948
installCount: number;
5049
rating: number;
5150
ratingCount: number;
52-
versions: IGalleryVersion[];
51+
assets: IGalleryExtensionAssets;
52+
downloadHeaders: { [key: string]: string; };
5353
}
5454

5555
export interface IGalleryMetadata {
@@ -98,6 +98,7 @@ export interface IExtensionGalleryService {
9898
_serviceBrand: any;
9999
isEnabled(): boolean;
100100
query(options?: IQueryOptions): TPromise<IPager<IGalleryExtension>>;
101+
download(extension: IGalleryExtension): TPromise<string>;
101102
}
102103

103104
export type InstallExtensionEvent = { id: string; gallery?: IGalleryExtension; };
@@ -111,8 +112,8 @@ export interface IExtensionManagementService {
111112
onUninstallExtension: Event<string>;
112113
onDidUninstallExtension: Event<string>;
113114

114-
install(extension: IGalleryExtension): TPromise<void>;
115115
install(zipPath: string): TPromise<void>;
116+
installFromGallery(extension: IGalleryExtension): TPromise<void>;
116117
uninstall(extension: ILocalExtension): TPromise<void>;
117118
getInstalled(): TPromise<ILocalExtension[]>;
118119
}

src/vs/platform/extensionManagement/common/extensionManagementIpc.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77

88
import { TPromise } from 'vs/base/common/winjs.base';
99
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
10-
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent } from './extensionManagement';
10+
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension } from './extensionManagement';
1111
import Event from 'vs/base/common/event';
1212

1313
export interface IExtensionManagementChannel extends IChannel {
1414
call(command: 'event:onInstallExtension'): TPromise<void>;
1515
call(command: 'event:onDidInstallExtension'): TPromise<void>;
1616
call(command: 'event:onUninstallExtension'): TPromise<void>;
1717
call(command: 'event:onDidUninstallExtension'): TPromise<void>;
18-
call(command: 'install', extensionOrPath: ILocalExtension | string): TPromise<ILocalExtension>;
18+
call(command: 'install', path: string): TPromise<void>;
19+
call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<void>;
1920
call(command: 'uninstall', extension: ILocalExtension): TPromise<void>;
2021
call(command: 'getInstalled'): TPromise<ILocalExtension[]>;
2122
call(command: string, arg: any): TPromise<any>;
@@ -32,6 +33,7 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel {
3233
case 'event:onUninstallExtension': return eventToCall(this.service.onUninstallExtension);
3334
case 'event:onDidUninstallExtension': return eventToCall(this.service.onDidUninstallExtension);
3435
case 'install': return this.service.install(arg);
36+
case 'installFromGallery': return this.service.installFromGallery(arg);
3537
case 'uninstall': return this.service.uninstall(arg);
3638
case 'getInstalled': return this.service.getInstalled();
3739
}
@@ -56,10 +58,12 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
5658
private _onDidUninstallExtension = eventFromCall<string>(this.channel, 'event:onDidUninstallExtension');
5759
get onDidUninstallExtension(): Event<string> { return this._onDidUninstallExtension; }
5860

59-
install(extension: IGalleryExtension): TPromise<void>;
60-
install(zipPath: string): TPromise<void>;
61-
install(arg: any): TPromise<void> {
62-
return this.channel.call('install', arg);
61+
install(zipPath: string): TPromise<void> {
62+
return this.channel.call('install', zipPath);
63+
}
64+
65+
installFromGallery(extension: IGalleryExtension): TPromise<void> {
66+
return this.channel.call('installFromGallery', extension);
6367
}
6468

6569
uninstall(extension: ILocalExtension): TPromise<void> {

src/vs/platform/extensionManagement/node/extensionGalleryService.ts

Lines changed: 111 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { localize } from 'vs/nls';
7+
import { tmpdir } from 'os';
8+
import * as path from 'path';
69
import { TPromise } from 'vs/base/common/winjs.base';
7-
import { IGalleryExtension, IExtensionGalleryService, IGalleryVersion, IQueryOptions, SortBy, SortOrder } from 'vs/platform/extensionManagement/common/extensionManagement';
10+
import { IGalleryExtension, IExtensionGalleryService, IQueryOptions, SortBy, SortOrder, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
811
import { isUndefined } from 'vs/base/common/types';
912
import { assign, getOrDefault } from 'vs/base/common/objects';
1013
import { IRequestService } from 'vs/platform/request/common/request';
1114
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1215
import { IPager } from 'vs/base/common/paging';
16+
import { download, json, IRequestOptions } from 'vs/base/node/request';
17+
import { getProxyAgent } from 'vs/base/node/proxy';
18+
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1319
import pkg from 'vs/platform/package';
1420
import product from 'vs/platform/product';
21+
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
1522

1623
interface IRawGalleryExtensionFile {
1724
assetType: string;
@@ -158,21 +165,21 @@ function getAssetSource(files: IRawGalleryExtensionFile[], type: string): string
158165
return result && result.source;
159166
}
160167

161-
function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, downloadHeaders: any): IGalleryExtension {
162-
const versions = galleryExtension.versions.map<IGalleryVersion>(v => ({
163-
version: v.version,
164-
date: v.lastUpdated,
165-
downloadHeaders,
166-
downloadUrl: `${ v.assetUri }/${ AssetType.VSIX }?install=true`,
167-
manifestUrl: `${ v.assetUri }/${ AssetType.Manifest }`,
168-
readmeUrl: `${ v.assetUri }/${ AssetType.Details }`,
169-
iconUrl: getAssetSource(v.files, AssetType.Icon) || require.toUrl('./media/defaultIcon.png'),
170-
licenseUrl: getAssetSource(v.files, AssetType.License)
171-
}));
168+
function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, downloadHeaders: { [key: string]: string; }): IGalleryExtension {
169+
const [version] = galleryExtension.versions;
170+
const assets = {
171+
manifest: getAssetSource(version.files, AssetType.Manifest),
172+
readme: getAssetSource(version.files, AssetType.Details),
173+
download: `${ getAssetSource(version.files, AssetType.VSIX) }?install=true`,
174+
icon: getAssetSource(version.files, AssetType.Icon) || require.toUrl('./media/defaultIcon.png'),
175+
license: getAssetSource(version.files, AssetType.License)
176+
};
172177

173178
return {
174179
id: galleryExtension.extensionId,
175180
name: galleryExtension.extensionName,
181+
version: version.version,
182+
date: version.lastUpdated,
176183
displayName: galleryExtension.displayName,
177184
publisherId: galleryExtension.publisher.publisherId,
178185
publisher: galleryExtension.publisher.publisherName,
@@ -181,7 +188,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
181188
installCount: getStatistic(galleryExtension.statistics, 'install'),
182189
rating: getStatistic(galleryExtension.statistics, 'averagerating'),
183190
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
184-
versions
191+
assets,
192+
downloadHeaders
185193
};
186194
}
187195

@@ -190,15 +198,29 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
190198
_serviceBrand: any;
191199

192200
private extensionsGalleryUrl: string;
193-
private machineId: TPromise<string>;
201+
202+
private getCommonHeaders(): TPromise<{ [key: string]: string; }> {
203+
return this.telemetryService.getTelemetryInfo().then(({ machineId }) => {
204+
const result: { [key: string]: string; } = {
205+
'X-Market-Client-Id': `VSCode ${ pkg.version }`,
206+
'User-Agent': `VSCode ${ pkg.version }`
207+
};
208+
209+
if (machineId) {
210+
result['X-Market-User-Id'] = machineId;
211+
}
212+
213+
return result;
214+
});
215+
}
194216

195217
constructor(
196218
@IRequestService private requestService: IRequestService,
197-
@ITelemetryService private telemetryService: ITelemetryService
219+
@ITelemetryService private telemetryService: ITelemetryService,
220+
@IConfigurationService private configurationService: IConfigurationService
198221
) {
199222
const config = product.extensionsGallery;
200223
this.extensionsGalleryUrl = config && config.serviceUrl;
201-
this.machineId = telemetryService.getTelemetryInfo().then(({ machineId }) => machineId);
202224
}
203225

204226
private api(path = ''): string {
@@ -221,10 +243,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
221243
this.telemetryService.publicLog('galleryService:query', { type, text });
222244

223245
let query = new Query()
224-
.withFlags(Flags.IncludeVersions, Flags.IncludeCategoryAndTags, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles)
246+
.withFlags(Flags.IncludeLatestVersionOnly, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles)
225247
.withPage(1, pageSize)
226248
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
227-
.withAssetTypes(AssetType.Icon, AssetType.License);
249+
.withAssetTypes(AssetType.Icon, AssetType.License, AssetType.Details, AssetType.Manifest, AssetType.VSIX);
228250

229251
if (text) {
230252
query = query.withFilter(FilterType.SearchText, text).withSortBy(SortBy.NoneOrRelevance);
@@ -245,7 +267,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
245267
}
246268

247269
return this.queryGallery(query).then(({ galleryExtensions, total }) => {
248-
return this.getRequestHeaders().then(downloadHeaders => {
270+
return this.getCommonHeaders().then(downloadHeaders => {
249271
const extensions = galleryExtensions.map(e => toExtension(e, this.extensionsGalleryUrl, downloadHeaders));
250272
const pageSize = query.pageSize;
251273
const getPage = pageIndex => this.queryGallery(query.withPage(pageIndex + 1))
@@ -258,27 +280,19 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
258280

259281
private queryGallery(query: Query): TPromise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> {
260282
const data = JSON.stringify(query.raw);
261-
262-
return this.getRequestHeaders()
263-
.then(headers => {
264-
headers = assign(headers, {
265-
'Content-Type': 'application/json',
266-
'Accept': 'application/json;api-version=3.0-preview.1',
267-
'Accept-Encoding': 'gzip',
268-
'Content-Length': data.length
269-
});
270-
271-
const request = {
272-
type: 'POST',
273-
url: this.api('/extensionquery'),
274-
data,
275-
headers
276-
};
277-
278-
return this.requestService.makeRequest(request);
279-
})
280-
.then(r => JSON.parse(r.responseText).results[0])
281-
.then(r => {
283+
const request = this.request(this.api('/extensionquery'));
284+
285+
return this.getCommonHeaders()
286+
.then(headers => assign(headers, {
287+
'Content-Type': 'application/json',
288+
'Accept': 'application/json;api-version=3.0-preview.1',
289+
'Accept-Encoding': 'gzip',
290+
'Content-Length': data.length
291+
}))
292+
.then(headers => assign(request, { type: 'POST', data, headers }))
293+
.then(() => json<any>(request))
294+
.then(result => {
295+
const r = result.results[0];
282296
const galleryExtensions = r.extensions;
283297
const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0];
284298
const total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0;
@@ -287,18 +301,67 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
287301
});
288302
}
289303

290-
private getRequestHeaders(): TPromise<any> {
291-
return this.machineId.then(machineId => {
292-
const result = {
293-
'X-Market-Client-Id': `VSCode ${ pkg.version }`,
294-
'User-Agent': `VSCode ${ pkg.version }`
304+
download(extension: IGalleryExtension): TPromise<string> {
305+
const query = new Query()
306+
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles)
307+
.withPage(1, 1)
308+
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
309+
.withAssetTypes(AssetType.Manifest, AssetType.VSIX)
310+
.withFilter(FilterType.ExtensionId, extension.id);
311+
312+
return this.queryGallery(query).then(({ galleryExtensions }) => {
313+
const [rawExtension] = galleryExtensions;
314+
315+
if (!rawExtension) {
316+
return TPromise.wrapError(new Error(localize('notFound', "Extension not found")));
317+
}
318+
319+
return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions).then(rawVersion => {
320+
const url = `${ getAssetSource(rawVersion.files, AssetType.VSIX) }?install=true`;
321+
const zipPath = path.join(tmpdir(), extension.id);
322+
const request = this.request(url);
323+
324+
return this.getCommonHeaders()
325+
.then(headers => assign(request, { headers }))
326+
.then(() => download(zipPath, request))
327+
.then(() => zipPath);
328+
});
329+
});
330+
}
331+
332+
private getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): TPromise<IRawGalleryExtensionVersion> {
333+
if (!versions.length) {
334+
return TPromise.wrapError(new Error(localize('noCompatible', "Couldn't find a compatible version of {0} with this version of Code.", extension.displayName || extension.extensionName)));
335+
}
336+
337+
const version = versions[0];
338+
const url = getAssetSource(version.files, AssetType.Manifest);
339+
let request = this.request(url);
340+
request = assign(request, { headers: { 'accept-encoding': 'gzip' } });
341+
342+
return json<IExtensionManifest>(request).then(manifest => {
343+
const desc = {
344+
isBuiltin: false,
345+
engines: { vscode: manifest.engines.vscode },
346+
main: manifest.main
295347
};
296348

297-
if (machineId) {
298-
result['X-Market-User-Id'] = machineId;
349+
if (!isValidExtensionVersion(pkg.version, desc, [])) {
350+
return this.getLastValidExtensionVersion(extension, versions.slice(1));
299351
}
300352

301-
return result;
353+
return version;
302354
});
303355
}
356+
357+
// Helper for proxy business... shameful.
358+
// This should be pushed down and not rely on the context service
359+
private request(url: string): IRequestOptions {
360+
const httpConfig = this.configurationService.getConfiguration<any>('http') || {};
361+
const proxyUrl = httpConfig.proxy as string;
362+
const strictSSL = httpConfig.proxyStrictSSL as boolean;
363+
const agent = getProxyAgent(url, { proxyUrl, strictSSL });
364+
365+
return { url, agent, strictSSL };
366+
}
304367
}

0 commit comments

Comments
 (0)