mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from master
This commit is contained in:
@@ -4,10 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getIdFromLocalExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
@@ -31,6 +30,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IExtensionManagementService private extensionManagementService: IExtensionManagementService
|
||||
) {
|
||||
extensionManagementService.onDidInstallExtension(this._onDidInstallExtension, this, this.disposables);
|
||||
extensionManagementService.onDidUninstallExtension(this._onDidUninstallExtension, this, this.disposables);
|
||||
}
|
||||
|
||||
@@ -102,26 +102,26 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
return true;
|
||||
}
|
||||
|
||||
setEnablement(arg: ILocalExtension | IExtensionIdentifier, newState: EnablementState): TPromise<boolean> {
|
||||
setEnablement(arg: ILocalExtension | IExtensionIdentifier, newState: EnablementState): Promise<boolean> {
|
||||
let identifier: IExtensionIdentifier;
|
||||
if (isIExtensionIdentifier(arg)) {
|
||||
identifier = arg;
|
||||
} else {
|
||||
if (!this.canChangeEnablement(arg)) {
|
||||
return TPromise.wrap(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
identifier = arg.galleryIdentifier;
|
||||
}
|
||||
|
||||
const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled;
|
||||
if (workspace && !this.hasWorkspace) {
|
||||
return TPromise.wrapError<boolean>(new Error(localize('noWorkspace', "No workspace.")));
|
||||
return Promise.reject(new Error(localize('noWorkspace', "No workspace.")));
|
||||
}
|
||||
|
||||
const currentState = this._getEnablementState(identifier);
|
||||
|
||||
if (currentState === newState) {
|
||||
return TPromise.as(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
}
|
||||
|
||||
this._onEnablementChanged.fire(identifier);
|
||||
return TPromise.as(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
isEnabled(extension: ILocalExtension): boolean {
|
||||
@@ -198,17 +198,17 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
this._removeFromEnabledExtensions(identifier, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
private _addToDisabledExtensions(identifier: IExtensionIdentifier, scope: StorageScope): TPromise<boolean> {
|
||||
private _addToDisabledExtensions(identifier: IExtensionIdentifier, scope: StorageScope): Promise<boolean> {
|
||||
if (scope === StorageScope.WORKSPACE && !this.hasWorkspace) {
|
||||
return TPromise.wrap(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
let disabledExtensions = this._getDisabledExtensions(scope);
|
||||
if (disabledExtensions.every(e => !areSameExtensions(e, identifier))) {
|
||||
disabledExtensions.push(identifier);
|
||||
this._setDisabledExtensions(disabledExtensions, scope, identifier);
|
||||
return TPromise.wrap(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return TPromise.wrap(false);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
private _removeFromDisabledExtensions(identifier: IExtensionIdentifier, scope: StorageScope): boolean {
|
||||
@@ -288,18 +288,32 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidInstallExtension(event: DidInstallExtensionEvent): void {
|
||||
if (event.local && event.operation === InstallOperation.Install) {
|
||||
const wasDisabled = !this.isEnabled(event.local);
|
||||
this._reset(event.local.galleryIdentifier);
|
||||
if (wasDisabled) {
|
||||
this._onEnablementChanged.fire(event.local.galleryIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void {
|
||||
if (!error) {
|
||||
const id = getIdFromLocalExtensionId(identifier.id);
|
||||
if (id) {
|
||||
const extension = { id, uuid: identifier.uuid };
|
||||
this._removeFromDisabledExtensions(extension, StorageScope.WORKSPACE);
|
||||
this._removeFromEnabledExtensions(extension, StorageScope.WORKSPACE);
|
||||
this._removeFromDisabledExtensions(extension, StorageScope.GLOBAL);
|
||||
this._reset(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _reset(extension: IExtensionIdentifier) {
|
||||
this._removeFromDisabledExtensions(extension, StorageScope.WORKSPACE);
|
||||
this._removeFromEnabledExtensions(extension, StorageScope.WORKSPACE);
|
||||
this._removeFromDisabledExtensions(extension, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILocalization } from 'vs/platform/localizations/common/localizations';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$';
|
||||
export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
|
||||
@@ -112,6 +110,8 @@ export interface IExtensionContributions {
|
||||
localizations?: ILocalization[];
|
||||
}
|
||||
|
||||
export type ExtensionKind = 'ui' | 'workspace';
|
||||
|
||||
export interface IExtensionManifest {
|
||||
name: string;
|
||||
publisher: string;
|
||||
@@ -123,9 +123,11 @@ export interface IExtensionManifest {
|
||||
main?: string;
|
||||
icon?: string;
|
||||
categories?: string[];
|
||||
keywords?: string[];
|
||||
activationEvents?: string[];
|
||||
extensionDependencies?: string[];
|
||||
extensionPack?: string[];
|
||||
extensionKind?: ExtensionKind;
|
||||
contributes?: IExtensionContributions;
|
||||
repository?: {
|
||||
url: string;
|
||||
@@ -139,6 +141,7 @@ export interface IGalleryExtensionProperties {
|
||||
dependencies?: string[];
|
||||
extensionPack?: string[];
|
||||
engine?: string;
|
||||
localizedLanguages?: string[];
|
||||
}
|
||||
|
||||
export interface IGalleryExtensionAsset {
|
||||
@@ -177,6 +180,11 @@ export interface IExtensionIdentifier {
|
||||
uuid?: string;
|
||||
}
|
||||
|
||||
export interface IGalleryExtensionVersion {
|
||||
version: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
export interface IGalleryExtension {
|
||||
name: string;
|
||||
identifier: IExtensionIdentifier;
|
||||
@@ -202,7 +210,7 @@ export interface IGalleryMetadata {
|
||||
publisherDisplayName: string;
|
||||
}
|
||||
|
||||
export enum LocalExtensionType {
|
||||
export const enum LocalExtensionType {
|
||||
System,
|
||||
User
|
||||
}
|
||||
@@ -221,7 +229,7 @@ export interface ILocalExtension {
|
||||
export const IExtensionManagementService = createDecorator<IExtensionManagementService>('extensionManagementService');
|
||||
export const IExtensionGalleryService = createDecorator<IExtensionGalleryService>('extensionGalleryService');
|
||||
|
||||
export enum SortBy {
|
||||
export const enum SortBy {
|
||||
NoneOrRelevance = 0,
|
||||
LastUpdatedDate = 1,
|
||||
Title = 2,
|
||||
@@ -232,7 +240,7 @@ export enum SortBy {
|
||||
WeightedRating = 12
|
||||
}
|
||||
|
||||
export enum SortOrder {
|
||||
export const enum SortOrder {
|
||||
Default = 0,
|
||||
Ascending = 1,
|
||||
Descending = 2
|
||||
@@ -248,7 +256,7 @@ export interface IQueryOptions {
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export enum StatisticType {
|
||||
export const enum StatisticType {
|
||||
Uninstall = 'uninstall'
|
||||
}
|
||||
|
||||
@@ -257,8 +265,9 @@ export interface IReportedExtension {
|
||||
malicious: boolean;
|
||||
}
|
||||
|
||||
export enum InstallOperation {
|
||||
Install = 1,
|
||||
export const enum InstallOperation {
|
||||
None = 0,
|
||||
Install,
|
||||
Update
|
||||
}
|
||||
|
||||
@@ -269,16 +278,18 @@ export interface ITranslation {
|
||||
export interface IExtensionGalleryService {
|
||||
_serviceBrand: any;
|
||||
isEnabled(): boolean;
|
||||
query(options?: IQueryOptions): TPromise<IPager<IGalleryExtension>>;
|
||||
download(extension: IGalleryExtension, operation: InstallOperation): TPromise<string>;
|
||||
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): TPromise<void>;
|
||||
getReadme(extension: IGalleryExtension): TPromise<string>;
|
||||
getManifest(extension: IGalleryExtension): TPromise<IExtensionManifest>;
|
||||
getChangelog(extension: IGalleryExtension): TPromise<string>;
|
||||
getCoreTranslation(extension: IGalleryExtension, languageId: string): TPromise<ITranslation>;
|
||||
loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension>;
|
||||
loadAllDependencies(dependencies: IExtensionIdentifier[]): TPromise<IGalleryExtension[]>;
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]>;
|
||||
query(options?: IQueryOptions): Promise<IPager<IGalleryExtension>>;
|
||||
download(extension: IGalleryExtension, operation: InstallOperation): Promise<string>;
|
||||
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>;
|
||||
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
|
||||
getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest>;
|
||||
getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
|
||||
getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation>;
|
||||
loadCompatibleVersion(extension: IGalleryExtension, fromVersion?: string): Promise<IGalleryExtension>;
|
||||
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]>;
|
||||
loadAllDependencies(dependencies: IExtensionIdentifier[], token: CancellationToken): Promise<IGalleryExtension[]>;
|
||||
getExtensionsReport(): Promise<IReportedExtension[]>;
|
||||
getExtension(id: IExtensionIdentifier, version?: string): Promise<IGalleryExtension>;
|
||||
}
|
||||
|
||||
export interface InstallExtensionEvent {
|
||||
@@ -301,6 +312,9 @@ export interface DidUninstallExtensionEvent {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const INSTALL_ERROR_MALICIOUS = 'malicious';
|
||||
export const INSTALL_ERROR_INCOMPATIBLE = 'incompatible';
|
||||
|
||||
export interface IExtensionManagementService {
|
||||
_serviceBrand: any;
|
||||
|
||||
@@ -309,31 +323,34 @@ export interface IExtensionManagementService {
|
||||
onUninstallExtension: Event<IExtensionIdentifier>;
|
||||
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
|
||||
|
||||
install(zipPath: string): TPromise<void>;
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void>;
|
||||
uninstall(extension: ILocalExtension, force?: boolean): TPromise<void>;
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<void>;
|
||||
getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]>;
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]>;
|
||||
zip(extension: ILocalExtension): Promise<URI>;
|
||||
unzip(zipLocation: URI, type: LocalExtensionType): Promise<IExtensionIdentifier>;
|
||||
install(vsix: URI): Promise<IExtensionIdentifier>;
|
||||
installFromGallery(extension: IGalleryExtension): Promise<void>;
|
||||
uninstall(extension: ILocalExtension, force?: boolean): Promise<void>;
|
||||
reinstallFromGallery(extension: ILocalExtension): Promise<void>;
|
||||
getInstalled(type?: LocalExtensionType): Promise<ILocalExtension[]>;
|
||||
getExtensionsReport(): Promise<IReportedExtension[]>;
|
||||
|
||||
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): TPromise<ILocalExtension>;
|
||||
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension>;
|
||||
}
|
||||
|
||||
export const IExtensionManagementServerService = createDecorator<IExtensionManagementServerService>('extensionManagementServerService');
|
||||
|
||||
export interface IExtensionManagementServer {
|
||||
extensionManagementService: IExtensionManagementService;
|
||||
location: URI;
|
||||
authority: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface IExtensionManagementServerService {
|
||||
_serviceBrand: any;
|
||||
readonly extensionManagementServers: IExtensionManagementServer[];
|
||||
getDefaultExtensionManagementServer(): IExtensionManagementServer;
|
||||
getExtensionManagementServer(location: URI): IExtensionManagementServer;
|
||||
readonly localExtensionManagementServer: IExtensionManagementServer | null;
|
||||
readonly remoteExtensionManagementServer: IExtensionManagementServer | null;
|
||||
getExtensionManagementServer(location: URI): IExtensionManagementServer | null;
|
||||
}
|
||||
|
||||
export enum EnablementState {
|
||||
export const enum EnablementState {
|
||||
Disabled,
|
||||
WorkspaceDisabled,
|
||||
Enabled,
|
||||
@@ -383,7 +400,7 @@ export interface IExtensionEnablementService {
|
||||
*
|
||||
* Throws error if enablement is requested for workspace and there is no workspace
|
||||
*/
|
||||
setEnablement(extension: ILocalExtension, state: EnablementState): TPromise<boolean>;
|
||||
setEnablement(extension: ILocalExtension, state: EnablementState): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IExtensionsConfigContent {
|
||||
@@ -413,17 +430,15 @@ export interface IExtensionTipsService {
|
||||
_serviceBrand: any;
|
||||
getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; };
|
||||
getFileBasedRecommendations(): IExtensionRecommendation[];
|
||||
getOtherRecommendations(): TPromise<IExtensionRecommendation[]>;
|
||||
getWorkspaceRecommendations(): TPromise<IExtensionRecommendation[]>;
|
||||
getOtherRecommendations(): Promise<IExtensionRecommendation[]>;
|
||||
getWorkspaceRecommendations(): Promise<IExtensionRecommendation[]>;
|
||||
getKeymapRecommendations(): IExtensionRecommendation[];
|
||||
getAllRecommendations(): TPromise<IExtensionRecommendation[]>;
|
||||
getKeywordsForExtension(extension: string): string[];
|
||||
toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void;
|
||||
getAllIgnoredRecommendations(): { global: string[], workspace: string[] };
|
||||
onRecommendationChange: Event<RecommendationChangeNotification>;
|
||||
}
|
||||
|
||||
export enum ExtensionRecommendationReason {
|
||||
export const enum ExtensionRecommendationReason {
|
||||
Workspace,
|
||||
File,
|
||||
Executable,
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from './extensionManagement';
|
||||
import { Event, buffer, mapEvent } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IURITransformer } from 'vs/base/common/uriIpc';
|
||||
|
||||
export interface IExtensionManagementChannel extends IChannel {
|
||||
listen(event: 'onInstallExtension'): Event<InstallExtensionEvent>;
|
||||
listen(event: 'onDidInstallExtension'): Event<DidInstallExtensionEvent>;
|
||||
listen(event: 'onUninstallExtension'): Event<IExtensionIdentifier>;
|
||||
listen(event: 'onDidUninstallExtension'): Event<DidUninstallExtensionEvent>;
|
||||
call(command: 'install', args: [string]): TPromise<void>;
|
||||
call(command: 'installFromGallery', args: [IGalleryExtension]): TPromise<void>;
|
||||
call(command: 'uninstall', args: [ILocalExtension, boolean]): TPromise<void>;
|
||||
call(command: 'reinstallFromGallery', args: [ILocalExtension]): TPromise<void>;
|
||||
call(command: 'getInstalled', args: [LocalExtensionType]): TPromise<ILocalExtension[]>;
|
||||
call(command: 'getExtensionsReport'): TPromise<IReportedExtension[]>;
|
||||
call(command: 'updateMetadata', args: [ILocalExtension, IGalleryMetadata]): TPromise<ILocalExtension>;
|
||||
}
|
||||
|
||||
export class ExtensionManagementChannel implements IExtensionManagementChannel {
|
||||
|
||||
onInstallExtension: Event<InstallExtensionEvent>;
|
||||
onDidInstallExtension: Event<DidInstallExtensionEvent>;
|
||||
onUninstallExtension: Event<IExtensionIdentifier>;
|
||||
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
|
||||
|
||||
constructor(private service: IExtensionManagementService) {
|
||||
this.onInstallExtension = buffer(service.onInstallExtension, true);
|
||||
this.onDidInstallExtension = buffer(service.onDidInstallExtension, true);
|
||||
this.onUninstallExtension = buffer(service.onUninstallExtension, true);
|
||||
this.onDidUninstallExtension = buffer(service.onDidUninstallExtension, true);
|
||||
}
|
||||
|
||||
listen(event: string): Event<any> {
|
||||
switch (event) {
|
||||
case 'onInstallExtension': return this.onInstallExtension;
|
||||
case 'onDidInstallExtension': return this.onDidInstallExtension;
|
||||
case 'onUninstallExtension': return this.onUninstallExtension;
|
||||
case 'onDidUninstallExtension': return this.onDidUninstallExtension;
|
||||
}
|
||||
|
||||
throw new Error('Invalid listen');
|
||||
}
|
||||
|
||||
call(command: string, args?: any): TPromise<any> {
|
||||
switch (command) {
|
||||
case 'install': return this.service.install(args[0]);
|
||||
case 'installFromGallery': return this.service.installFromGallery(args[0]);
|
||||
case 'uninstall': return this.service.uninstall(this._transform(args[0]), args[1]);
|
||||
case 'reinstallFromGallery': return this.service.reinstallFromGallery(this._transform(args[0]));
|
||||
case 'getInstalled': return this.service.getInstalled(args[0]);
|
||||
case 'updateMetadata': return this.service.updateMetadata(this._transform(args[0]), args[1]);
|
||||
case 'getExtensionsReport': return this.service.getExtensionsReport();
|
||||
}
|
||||
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
|
||||
private _transform(extension: ILocalExtension): ILocalExtension {
|
||||
return extension ? { ...extension, ...{ location: URI.revive(extension.location) } } : extension;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionManagementChannelClient implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
constructor(private channel: IExtensionManagementChannel, private uriTransformer: IURITransformer) { }
|
||||
|
||||
get onInstallExtension(): Event<InstallExtensionEvent> { return this.channel.listen('onInstallExtension'); }
|
||||
get onDidInstallExtension(): Event<DidInstallExtensionEvent> { return mapEvent(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: this._transformIncoming(i.local) })); }
|
||||
get onUninstallExtension(): Event<IExtensionIdentifier> { return this.channel.listen('onUninstallExtension'); }
|
||||
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this.channel.listen('onDidUninstallExtension'); }
|
||||
|
||||
install(zipPath: string): TPromise<void> {
|
||||
return this.channel.call('install', [zipPath]);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
return this.channel.call('installFromGallery', [extension]);
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension, force = false): TPromise<void> {
|
||||
return this.channel.call('uninstall', [this._transformOutgoing(extension), force]);
|
||||
}
|
||||
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<void> {
|
||||
return this.channel.call('reinstallFromGallery', [this._transformOutgoing(extension)]);
|
||||
}
|
||||
|
||||
getInstalled(type: LocalExtensionType = null): TPromise<ILocalExtension[]> {
|
||||
return this.channel.call('getInstalled', [type])
|
||||
.then(extensions => extensions.map(extension => this._transformIncoming(extension)));
|
||||
}
|
||||
|
||||
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): TPromise<ILocalExtension> {
|
||||
return this.channel.call('updateMetadata', [this._transformOutgoing(local), metadata])
|
||||
.then(extension => this._transformIncoming(extension));
|
||||
}
|
||||
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]> {
|
||||
return this.channel.call('getExtensionsReport');
|
||||
}
|
||||
|
||||
private _transformIncoming(extension: ILocalExtension): ILocalExtension {
|
||||
return extension ? { ...extension, ...{ location: URI.revive(this.uriTransformer.transformIncoming(extension.location)) } } : extension;
|
||||
}
|
||||
|
||||
private _transformOutgoing(extension: ILocalExtension): ILocalExtension {
|
||||
return extension ? { ...extension, ...{ location: this.uriTransformer.transformOutgoing(extension.location) } } : extension;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,9 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ILocalExtension, IGalleryExtension, EXTENSION_IDENTIFIER_REGEX, IExtensionIdentifier, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { compareIgnoreCase } from 'vs/base/common/strings';
|
||||
|
||||
export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean {
|
||||
if (a.uuid && b.uuid) {
|
||||
@@ -14,7 +13,11 @@ export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifi
|
||||
if (a.id === b.id) {
|
||||
return true;
|
||||
}
|
||||
return adoptToGalleryExtensionId(a.id) === adoptToGalleryExtensionId(b.id);
|
||||
return compareIgnoreCase(a.id, b.id) === 0;
|
||||
}
|
||||
|
||||
export function adoptToGalleryExtensionId(id: string): string {
|
||||
return id.toLocaleLowerCase();
|
||||
}
|
||||
|
||||
export function getGalleryExtensionId(publisher: string, name: string): string {
|
||||
@@ -35,10 +38,6 @@ export function getIdFromLocalExtensionId(localExtensionId: string): string {
|
||||
return adoptToGalleryExtensionId(localExtensionId);
|
||||
}
|
||||
|
||||
export function adoptToGalleryExtensionId(id: string): string {
|
||||
return id.replace(EXTENSION_IDENTIFIER_REGEX, (match, publisher: string, name: string) => getGalleryExtensionId(publisher, name));
|
||||
}
|
||||
|
||||
export function getLocalExtensionId(id: string, version: string): string {
|
||||
return `${id}-${version}`;
|
||||
}
|
||||
@@ -99,12 +98,11 @@ export function getGalleryExtensionTelemetryData(extension: IGalleryExtension):
|
||||
publisherId: extension.publisherId,
|
||||
publisherName: extension.publisher,
|
||||
publisherDisplayName: extension.publisherDisplayName,
|
||||
dependencies: extension.properties.dependencies.length > 0,
|
||||
dependencies: !!(extension.properties.dependencies && extension.properties.dependencies.length > 0),
|
||||
...extension.telemetryData
|
||||
};
|
||||
}
|
||||
|
||||
export const BetterMergeDisabledNowKey = 'extensions/bettermergedisablednow';
|
||||
export const BetterMergeId = 'pprice.better-merge';
|
||||
|
||||
export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set<string> {
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Event, EventMultiplexer } from 'vs/base/common/event';
|
||||
import {
|
||||
IExtensionManagementService, ILocalExtension, IGalleryExtension, LocalExtensionType, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata,
|
||||
IExtensionManagementServerService, IExtensionManagementServer
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
|
||||
export class MulitExtensionManagementService implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
onInstallExtension: Event<InstallExtensionEvent>;
|
||||
onDidInstallExtension: Event<DidInstallExtensionEvent>;
|
||||
onUninstallExtension: Event<IExtensionIdentifier>;
|
||||
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
|
||||
|
||||
private readonly servers: IExtensionManagementServer[];
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService
|
||||
) {
|
||||
this.servers = this.extensionManagementServerService.extensionManagementServers;
|
||||
this.onInstallExtension = this.servers.reduce((emitter: EventMultiplexer<InstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer<InstallExtensionEvent>()).event;
|
||||
this.onDidInstallExtension = this.servers.reduce((emitter: EventMultiplexer<DidInstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer<DidInstallExtensionEvent>()).event;
|
||||
this.onUninstallExtension = this.servers.reduce((emitter: EventMultiplexer<IExtensionIdentifier>, server) => { emitter.add(server.extensionManagementService.onUninstallExtension); return emitter; }, new EventMultiplexer<IExtensionIdentifier>()).event;
|
||||
this.onDidUninstallExtension = this.servers.reduce((emitter: EventMultiplexer<DidUninstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer<DidUninstallExtensionEvent>()).event;
|
||||
}
|
||||
|
||||
getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]> {
|
||||
return TPromise.join(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type)))
|
||||
.then(result => flatten(result));
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension, force?: boolean): TPromise<void> {
|
||||
return this.getServer(extension).extensionManagementService.uninstall(extension, force);
|
||||
}
|
||||
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<void> {
|
||||
return this.getServer(extension).extensionManagementService.reinstallFromGallery(extension);
|
||||
}
|
||||
|
||||
updateMetadata(extension: ILocalExtension, metadata: IGalleryMetadata): TPromise<ILocalExtension> {
|
||||
return this.getServer(extension).extensionManagementService.updateMetadata(extension, metadata);
|
||||
}
|
||||
|
||||
install(zipPath: string): TPromise<void> {
|
||||
return this.servers[0].extensionManagementService.install(zipPath);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
return TPromise.join(this.servers.map(server => server.extensionManagementService.installFromGallery(extension))).then(() => null);
|
||||
}
|
||||
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]> {
|
||||
return this.servers[0].extensionManagementService.getExtensionsReport();
|
||||
}
|
||||
|
||||
private getServer(extension: ILocalExtension): IExtensionManagementServer {
|
||||
return this.extensionManagementServerService.getExtensionManagementServer(extension.location);
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { tmpdir } from 'os';
|
||||
import * as path from 'path';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/common/errors';
|
||||
import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { assign, getOrDefault } from 'vs/base/common/objects';
|
||||
import { IRequestService } from 'vs/platform/request/node/request';
|
||||
@@ -27,6 +25,9 @@ import { values } from 'vs/base/common/map';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
interface IRawGalleryExtensionFile {
|
||||
assetType: string;
|
||||
@@ -121,7 +122,8 @@ const AssetType = {
|
||||
const PropertyType = {
|
||||
Dependency: 'Microsoft.VisualStudio.Code.ExtensionDependencies',
|
||||
ExtensionPack: 'Microsoft.VisualStudio.Code.ExtensionPack',
|
||||
Engine: 'Microsoft.VisualStudio.Code.Engine'
|
||||
Engine: 'Microsoft.VisualStudio.Code.Engine',
|
||||
LocalizedLanguages: 'Microsoft.VisualStudio.Code.LocalizedLanguages'
|
||||
};
|
||||
|
||||
interface ICriterium {
|
||||
@@ -296,18 +298,23 @@ function getEngine(version: IRawGalleryExtensionVersion): string {
|
||||
return (values.length > 0 && values[0].value) || '';
|
||||
}
|
||||
|
||||
function getLocalizedLanguages(version: IRawGalleryExtensionVersion): string[] {
|
||||
const values = version.properties ? version.properties.filter(p => p.key === PropertyType.LocalizedLanguages) : [];
|
||||
const value = (values.length > 0 && values[0].value) || '';
|
||||
return value ? value.split(',') : [];
|
||||
}
|
||||
|
||||
function getIsPreview(flags: string): boolean {
|
||||
return flags.indexOf('preview') !== -1;
|
||||
}
|
||||
|
||||
function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, index: number, query: Query, querySource?: string): IGalleryExtension {
|
||||
const [version] = galleryExtension.versions;
|
||||
function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, index: number, query: Query, querySource?: string): IGalleryExtension {
|
||||
const assets = {
|
||||
manifest: getVersionAsset(version, AssetType.Manifest),
|
||||
readme: getVersionAsset(version, AssetType.Details),
|
||||
changelog: getVersionAsset(version, AssetType.Changelog),
|
||||
download: getVersionAsset(version, AssetType.VSIX),
|
||||
// {{SQL CARBON EDIT}}
|
||||
// {{SQL CARBON EDIT}} - Add downloadPage
|
||||
downloadPage: getVersionAsset(version, AssetType.DownloadPage),
|
||||
icon: getVersionAsset(version, AssetType.Icon),
|
||||
license: getVersionAsset(version, AssetType.License),
|
||||
@@ -335,7 +342,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
|
||||
properties: {
|
||||
dependencies: getExtensions(version, PropertyType.Dependency),
|
||||
extensionPack: getExtensions(version, PropertyType.ExtensionPack),
|
||||
engine: getEngine(version)
|
||||
engine: getEngine(version),
|
||||
localizedLanguages: getLocalizedLanguages(version)
|
||||
},
|
||||
/* __GDPR__FRAGMENT__
|
||||
"GalleryExtensionTelemetryData2" : {
|
||||
@@ -365,10 +373,11 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
private extensionsGalleryUrl: string;
|
||||
private extensionsControlUrl: string;
|
||||
|
||||
private readonly commonHeadersPromise: TPromise<{ [key: string]: string; }>;
|
||||
private readonly commonHeadersPromise: Promise<{ [key: string]: string; }>;
|
||||
|
||||
constructor(
|
||||
@IRequestService private requestService: IRequestService,
|
||||
@ILogService private logService: ILogService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -389,9 +398,34 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
return !!this.extensionsGalleryUrl;
|
||||
}
|
||||
|
||||
query(options: IQueryOptions = {}): TPromise<IPager<IGalleryExtension>> {
|
||||
getExtension({ id, uuid }: IExtensionIdentifier, version?: string): Promise<IGalleryExtension> {
|
||||
let query = new Query()
|
||||
.withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties)
|
||||
.withPage(1, 1)
|
||||
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
|
||||
.withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished));
|
||||
|
||||
if (uuid) {
|
||||
query = query.withFilter(FilterType.ExtensionId, uuid);
|
||||
} else {
|
||||
query = query.withFilter(FilterType.ExtensionName, id);
|
||||
}
|
||||
|
||||
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => {
|
||||
if (galleryExtensions.length) {
|
||||
const galleryExtension = galleryExtensions[0];
|
||||
const versionAsset = version ? galleryExtension.versions.filter(v => v.version === version)[0] : galleryExtension.versions[0];
|
||||
if (versionAsset) {
|
||||
return toExtension(galleryExtension, versionAsset, 0, query);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
query(options: IQueryOptions = {}): Promise<IPager<IGalleryExtension>> {
|
||||
if (!this.isEnabled()) {
|
||||
return TPromise.wrapError<IPager<IGalleryExtension>>(new Error('No extension gallery service configured.'));
|
||||
return Promise.reject(new Error('No extension gallery service configured.'));
|
||||
}
|
||||
|
||||
const type = options.names ? 'ids' : (options.text ? 'text' : 'all');
|
||||
@@ -449,18 +483,21 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
query = query.withSortOrder(options.sortOrder);
|
||||
}
|
||||
|
||||
return this.queryGallery(query).then(({ galleryExtensions, total }) => {
|
||||
const extensions = galleryExtensions.map((e, index) => toExtension(e, this.extensionsGalleryUrl, index, query, options.source));
|
||||
|
||||
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions, total }) => {
|
||||
const extensions = galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, query, options.source));
|
||||
// {{SQL CARBON EDIT}}
|
||||
const pageSize = extensions.length;
|
||||
const getPage = (pageIndex: number) => {
|
||||
const getPage = (pageIndex: number, ct: CancellationToken) => {
|
||||
if (ct.isCancellationRequested) {
|
||||
return Promise.reject(canceled());
|
||||
}
|
||||
|
||||
const nextPageQuery = query.withPage(pageIndex + 1);
|
||||
return this.queryGallery(nextPageQuery)
|
||||
.then(({ galleryExtensions }) => galleryExtensions.map((e, index) => toExtension(e, this.extensionsGalleryUrl, index, nextPageQuery, options.source)));
|
||||
return this.queryGallery(nextPageQuery, ct)
|
||||
.then(({ galleryExtensions }) => galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, nextPageQuery, options.source)));
|
||||
};
|
||||
|
||||
return { firstPage: extensions, total, pageSize, getPage };
|
||||
return { firstPage: extensions, total, pageSize, getPage } as IPager<IGalleryExtension>;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -544,7 +581,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
return a[fieldName] < b[fieldName] ? -1 : 1;
|
||||
}
|
||||
|
||||
private queryGallery(query: Query): TPromise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> {
|
||||
private queryGallery(query: Query, token: CancellationToken): Promise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> {
|
||||
return this.commonHeadersPromise.then(commonHeaders => {
|
||||
const data = JSON.stringify(query.raw);
|
||||
const headers = assign({}, commonHeaders, {
|
||||
@@ -560,7 +597,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
url: this.api('/extensionquery'),
|
||||
data,
|
||||
headers
|
||||
}).then(context => {
|
||||
}, token).then(context => {
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
let extensionPolicy: string = this.configurationService.getValue<string>(ExtensionsPolicyKey);
|
||||
@@ -583,9 +620,9 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
});
|
||||
}
|
||||
|
||||
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): TPromise<void> {
|
||||
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void> {
|
||||
if (!this.isEnabled()) {
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return this.commonHeadersPromise.then(commonHeaders => {
|
||||
@@ -595,79 +632,101 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
type: 'POST',
|
||||
url: this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`),
|
||||
headers
|
||||
}).then(null, () => null);
|
||||
}, CancellationToken.None).then(null, () => null);
|
||||
});
|
||||
}
|
||||
|
||||
download(extension: IGalleryExtension, operation: InstallOperation): TPromise<string> {
|
||||
return this.loadCompatibleVersion(extension)
|
||||
.then(extension => {
|
||||
if (!extension) {
|
||||
return TPromise.wrapError(new Error(localize('notCompatibleDownload', "Unable to download because the extension compatible with current version '{0}' of VS Code is not found.", pkg.version)));
|
||||
}
|
||||
const zipPath = path.join(tmpdir(), generateUuid());
|
||||
const data = getGalleryExtensionTelemetryData(extension);
|
||||
const startTime = new Date().getTime();
|
||||
/* __GDPR__
|
||||
"galleryService:downloadVSIX" : {
|
||||
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
const log = (duration: number) => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration }));
|
||||
download(extension: IGalleryExtension, operation: InstallOperation): Promise<string> {
|
||||
this.logService.trace('ExtensionGalleryService#download', extension.identifier.id);
|
||||
const zipPath = path.join(tmpdir(), generateUuid());
|
||||
const data = getGalleryExtensionTelemetryData(extension);
|
||||
const startTime = new Date().getTime();
|
||||
/* __GDPR__
|
||||
"galleryService:downloadVSIX" : {
|
||||
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
const log = (duration: number) => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration }));
|
||||
|
||||
// {{SQL Carbon Edit}} - Don't append install or update on to the URL
|
||||
// const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : '';
|
||||
const operationParam = undefined;
|
||||
const downloadAsset = operationParam ? {
|
||||
uri: `${extension.assets.download.uri}&${operationParam}=true`,
|
||||
fallbackUri: `${extension.assets.download.fallbackUri}?${operationParam}=true`
|
||||
} : extension.assets.download;
|
||||
// {{SQL Carbon Edit}} - Don't append install or update on to the URL
|
||||
// const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : '';
|
||||
const operationParam = undefined;
|
||||
const downloadAsset = operationParam ? {
|
||||
uri: `${extension.assets.download.uri}&${operationParam}=true`,
|
||||
fallbackUri: `${extension.assets.download.fallbackUri}?${operationParam}=true`
|
||||
} : extension.assets.download;
|
||||
|
||||
return this.getAsset(downloadAsset)
|
||||
.then(context => download(zipPath, context))
|
||||
.then(() => log(new Date().getTime() - startTime))
|
||||
.then(() => zipPath);
|
||||
});
|
||||
return this.getAsset(downloadAsset)
|
||||
.then(context => download(zipPath, context))
|
||||
.then(() => log(new Date().getTime() - startTime))
|
||||
.then(() => zipPath);
|
||||
}
|
||||
|
||||
getReadme(extension: IGalleryExtension): TPromise<string> {
|
||||
return this.getAsset(extension.assets.readme)
|
||||
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string> {
|
||||
return this.getAsset(extension.assets.readme, {}, token)
|
||||
.then(asText);
|
||||
}
|
||||
|
||||
getManifest(extension: IGalleryExtension): TPromise<IExtensionManifest> {
|
||||
return this.getAsset(extension.assets.manifest)
|
||||
getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest> {
|
||||
return this.getAsset(extension.assets.manifest, {}, token)
|
||||
.then(asText)
|
||||
.then(JSON.parse);
|
||||
}
|
||||
|
||||
getCoreTranslation(extension: IGalleryExtension, languageId: string): TPromise<ITranslation> {
|
||||
getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation> {
|
||||
const asset = extension.assets.coreTranslations[languageId.toUpperCase()];
|
||||
if (asset) {
|
||||
return this.getAsset(asset)
|
||||
.then(asText)
|
||||
.then(JSON.parse);
|
||||
}
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
getChangelog(extension: IGalleryExtension): TPromise<string> {
|
||||
return this.getAsset(extension.assets.changelog)
|
||||
getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise<string> {
|
||||
return this.getAsset(extension.assets.changelog, {}, token)
|
||||
.then(asText);
|
||||
}
|
||||
|
||||
loadAllDependencies(extensions: IExtensionIdentifier[]): TPromise<IGalleryExtension[]> {
|
||||
return this.getDependenciesReccursively(extensions.map(e => e.id), []);
|
||||
loadAllDependencies(extensions: IExtensionIdentifier[], token: CancellationToken): Promise<IGalleryExtension[]> {
|
||||
return this.getDependenciesReccursively(extensions.map(e => e.id), [], token);
|
||||
}
|
||||
|
||||
loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension> {
|
||||
if (extension.properties.engine && isEngineValid(extension.properties.engine)) {
|
||||
return TPromise.wrap(extension);
|
||||
getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise<IGalleryExtensionVersion[]> {
|
||||
let query = new Query()
|
||||
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties)
|
||||
.withPage(1, 1)
|
||||
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
|
||||
.withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished));
|
||||
|
||||
if (extension.identifier.uuid) {
|
||||
query = query.withFilter(FilterType.ExtensionId, extension.identifier.uuid);
|
||||
} else {
|
||||
query = query.withFilter(FilterType.ExtensionName, extension.identifier.id);
|
||||
}
|
||||
|
||||
return this.queryGallery(query, CancellationToken.None).then(({ galleryExtensions }) => {
|
||||
if (galleryExtensions.length) {
|
||||
if (compatible) {
|
||||
return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine) ? v : null)))
|
||||
.then(versions => versions
|
||||
.filter(v => !!v)
|
||||
.map(v => ({ version: v.version, date: v.lastUpdated })));
|
||||
} else {
|
||||
return galleryExtensions[0].versions.map(v => ({ version: v.version, date: v.lastUpdated }));
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
loadCompatibleVersion(extension: IGalleryExtension, fromVersion: string = extension.version): Promise<IGalleryExtension> {
|
||||
if (extension.version === fromVersion && extension.properties.engine && isEngineValid(extension.properties.engine)) {
|
||||
return Promise.resolve(extension);
|
||||
}
|
||||
const query = new Query()
|
||||
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties)
|
||||
.withPage(1, 1)
|
||||
@@ -676,7 +735,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
.withAssetTypes(AssetType.Manifest, AssetType.VSIX)
|
||||
.withFilter(FilterType.ExtensionId, extension.identifier.uuid);
|
||||
|
||||
return this.queryGallery(query)
|
||||
return this.queryGallery(query, CancellationToken.None)
|
||||
.then(({ galleryExtensions }) => {
|
||||
const [rawExtension] = galleryExtensions;
|
||||
|
||||
@@ -684,25 +743,43 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions)
|
||||
const versions: IRawGalleryExtensionVersion[] = this.getVersionsFrom(rawExtension.versions, fromVersion);
|
||||
if (!versions.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getLastValidExtensionVersion(rawExtension, versions)
|
||||
.then(rawVersion => {
|
||||
if (rawVersion) {
|
||||
extension.properties.dependencies = getExtensions(rawVersion, PropertyType.Dependency);
|
||||
extension.properties.engine = getEngine(rawVersion);
|
||||
// {{SQL CARBON EDIT}}
|
||||
extension.assets.download = getVersionAsset(rawVersion, AssetType.VSIX);
|
||||
extension.assets.downloadPage = getVersionAsset(rawVersion, AssetType.DownloadPage);
|
||||
extension.version = rawVersion.version;
|
||||
return extension;
|
||||
return toExtension(rawExtension, rawVersion, 0, query);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private loadDependencies(extensionNames: string[]): TPromise<IGalleryExtension[]> {
|
||||
private getVersionsFrom(versions: IRawGalleryExtensionVersion[], version: string): IRawGalleryExtensionVersion[] {
|
||||
if (versions[0].version === version) {
|
||||
return versions;
|
||||
}
|
||||
const result: IRawGalleryExtensionVersion[] = [];
|
||||
let currentVersion: IRawGalleryExtensionVersion = null;
|
||||
for (const v of versions) {
|
||||
if (!currentVersion) {
|
||||
if (v.version === version) {
|
||||
currentVersion = v;
|
||||
}
|
||||
}
|
||||
if (currentVersion) {
|
||||
result.push(v);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private loadDependencies(extensionNames: string[], token: CancellationToken): Promise<IGalleryExtension[]> {
|
||||
if (!extensionNames || extensionNames.length === 0) {
|
||||
return TPromise.as([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
let query = new Query()
|
||||
@@ -713,14 +790,14 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
.withAssetTypes(AssetType.Icon, AssetType.License, AssetType.Details, AssetType.Manifest, AssetType.VSIX)
|
||||
.withFilter(FilterType.ExtensionName, ...extensionNames);
|
||||
|
||||
return this.queryGallery(query).then(result => {
|
||||
const dependencies = [];
|
||||
const ids = [];
|
||||
return this.queryGallery(query, token).then(result => {
|
||||
const dependencies: IGalleryExtension[] = [];
|
||||
const ids: string[] = [];
|
||||
|
||||
for (let index = 0; index < result.galleryExtensions.length; index++) {
|
||||
const rawExtension = result.galleryExtensions[index];
|
||||
if (ids.indexOf(rawExtension.extensionId) === -1) {
|
||||
dependencies.push(toExtension(rawExtension, this.extensionsGalleryUrl, index, query, 'dependencies'));
|
||||
dependencies.push(toExtension(rawExtension, rawExtension.versions[0], index, query, 'dependencies'));
|
||||
ids.push(rawExtension.extensionId);
|
||||
}
|
||||
}
|
||||
@@ -728,16 +805,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
});
|
||||
}
|
||||
|
||||
private getDependenciesReccursively(toGet: string[], result: IGalleryExtension[]): TPromise<IGalleryExtension[]> {
|
||||
private getDependenciesReccursively(toGet: string[], result: IGalleryExtension[], token: CancellationToken): Promise<IGalleryExtension[]> {
|
||||
if (!toGet || !toGet.length) {
|
||||
return TPromise.wrap(result);
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
toGet = result.length ? toGet.filter(e => !ExtensionGalleryService.hasExtensionByName(result, e)) : toGet;
|
||||
if (!toGet.length) {
|
||||
return TPromise.wrap(result);
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
return this.loadDependencies(toGet)
|
||||
return this.loadDependencies(toGet, token)
|
||||
.then(loadedDependencies => {
|
||||
const dependenciesSet = new Set<string>();
|
||||
for (const dep of loadedDependencies) {
|
||||
@@ -748,11 +825,11 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
result = distinct(result.concat(loadedDependencies), d => d.identifier.uuid);
|
||||
const dependencies: string[] = [];
|
||||
dependenciesSet.forEach(d => !ExtensionGalleryService.hasExtensionByName(result, d) && dependencies.push(d));
|
||||
return this.getDependenciesReccursively(dependencies, result);
|
||||
return this.getDependenciesReccursively(dependencies, result, token);
|
||||
});
|
||||
}
|
||||
|
||||
private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}): TPromise<IRequestContext> {
|
||||
private getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): Promise<IRequestContext> {
|
||||
return this.commonHeadersPromise.then(commonHeaders => {
|
||||
const baseOptions = { type: 'GET' };
|
||||
const headers = assign({}, commonHeaders, options.headers || {});
|
||||
@@ -762,18 +839,18 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
const fallbackUrl = asset.fallbackUri;
|
||||
const firstOptions = assign({}, options, { url });
|
||||
|
||||
return this.requestService.request(firstOptions)
|
||||
return this.requestService.request(firstOptions, token)
|
||||
.then(context => {
|
||||
if (context.res.statusCode === 200) {
|
||||
return TPromise.as(context);
|
||||
return Promise.resolve(context);
|
||||
}
|
||||
|
||||
return asText(context)
|
||||
.then(message => TPromise.wrapError<IRequestContext>(new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`)));
|
||||
.then(message => Promise.reject(new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`)));
|
||||
})
|
||||
.then(null, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError<IRequestContext>(err);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
const message = getErrorMessage(err);
|
||||
@@ -794,9 +871,9 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
this.telemetryService.publicLog('galleryService:cdnFallback', { url, message });
|
||||
|
||||
const fallbackOptions = assign({}, options, { url: fallbackUrl });
|
||||
return this.requestService.request(fallbackOptions).then(null, err => {
|
||||
return this.requestService.request(fallbackOptions, token).then(null, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError<IRequestContext>(err);
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
const message = getErrorMessage(err);
|
||||
@@ -808,13 +885,13 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('galleryService:requestError', { url: fallbackUrl, cdn: false, message });
|
||||
return TPromise.wrapError<IRequestContext>(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): TPromise<IRawGalleryExtensionVersion> {
|
||||
private getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion> {
|
||||
const version = this.getLastValidExtensionVersionFromProperties(extension, versions);
|
||||
if (version) {
|
||||
return version;
|
||||
@@ -822,39 +899,47 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
return this.getLastValidExtensionVersionReccursively(extension, versions);
|
||||
}
|
||||
|
||||
private getLastValidExtensionVersionFromProperties(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): TPromise<IRawGalleryExtensionVersion> {
|
||||
private getLastValidExtensionVersionFromProperties(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion> {
|
||||
for (const version of versions) {
|
||||
const engine = getEngine(version);
|
||||
if (!engine) {
|
||||
return null;
|
||||
}
|
||||
if (isEngineValid(engine)) {
|
||||
return TPromise.wrap(version);
|
||||
return Promise.resolve(version);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private getLastValidExtensionVersionReccursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): TPromise<IRawGalleryExtensionVersion> {
|
||||
if (!versions.length) {
|
||||
return null;
|
||||
private getEngine(version: IRawGalleryExtensionVersion): Promise<string> {
|
||||
const engine = getEngine(version);
|
||||
if (engine) {
|
||||
return Promise.resolve(engine);
|
||||
}
|
||||
|
||||
const version = versions[0];
|
||||
const asset = getVersionAsset(version, AssetType.Manifest);
|
||||
const headers = { 'Accept-Encoding': 'gzip' };
|
||||
|
||||
return this.getAsset(asset, { headers })
|
||||
.then(context => asJson<IExtensionManifest>(context))
|
||||
.then(manifest => {
|
||||
const engine = manifest.engines.vscode;
|
||||
.then(manifest => manifest.engines.vscode);
|
||||
}
|
||||
|
||||
private getLastValidExtensionVersionReccursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise<IRawGalleryExtensionVersion> {
|
||||
if (!versions.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const version = versions[0];
|
||||
return this.getEngine(version)
|
||||
.then(engine => {
|
||||
if (!isEngineValid(engine)) {
|
||||
return this.getLastValidExtensionVersionReccursively(extension, versions.slice(1));
|
||||
}
|
||||
|
||||
version.properties = version.properties || [];
|
||||
version.properties.push({ key: PropertyType.Engine, value: manifest.engines.vscode });
|
||||
version.properties.push({ key: PropertyType.Engine, value: engine });
|
||||
return version;
|
||||
});
|
||||
}
|
||||
@@ -868,18 +953,18 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
return false;
|
||||
}
|
||||
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]> {
|
||||
getExtensionsReport(): Promise<IReportedExtension[]> {
|
||||
if (!this.isEnabled()) {
|
||||
return TPromise.wrapError(new Error('No extension gallery service configured.'));
|
||||
return Promise.reject(new Error('No extension gallery service configured.'));
|
||||
}
|
||||
|
||||
if (!this.extensionsControlUrl) {
|
||||
return TPromise.as([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
return this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }).then(context => {
|
||||
return this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }, CancellationToken.None).then(context => {
|
||||
if (context.res.statusCode !== 200) {
|
||||
return TPromise.wrapError(new Error('Could not get extensions report.'));
|
||||
return Promise.reject(new Error('Could not get extensions report.'));
|
||||
}
|
||||
|
||||
return asJson<IRawExtensionsReport>(context).then(result => {
|
||||
@@ -891,13 +976,13 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
map.set(id, ext);
|
||||
}
|
||||
|
||||
return TPromise.as(values(map));
|
||||
return Promise.resolve(values(map));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveMarketplaceHeaders(environmentService: IEnvironmentService): TPromise<{ [key: string]: string; }> {
|
||||
export function resolveMarketplaceHeaders(environmentService: IEnvironmentService): Promise<{ [key: string]: string; }> {
|
||||
const marketplaceMachineIdFile = path.join(environmentService.userDataPath, 'machineid');
|
||||
|
||||
return readFile(marketplaceMachineIdFile, 'utf8').then(contents => {
|
||||
@@ -905,9 +990,9 @@ export function resolveMarketplaceHeaders(environmentService: IEnvironmentServic
|
||||
return contents;
|
||||
}
|
||||
|
||||
return TPromise.wrap(null); // invalid marketplace UUID
|
||||
return Promise.resolve(null); // invalid marketplace UUID
|
||||
}, error => {
|
||||
return TPromise.wrap(null); // error reading ID file
|
||||
return Promise.resolve(null); // error reading ID file
|
||||
}).then(uuid => {
|
||||
if (!uuid) {
|
||||
uuid = generateUuid();
|
||||
@@ -925,4 +1010,4 @@ export function resolveMarketplaceHeaders(environmentService: IEnvironmentServic
|
||||
'X-Market-User-Id': uuid
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { fork, ChildProcess } from 'child_process';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
@@ -14,97 +12,106 @@ import { posix } from 'path';
|
||||
import { Limiter } from 'vs/base/common/async';
|
||||
import { fromNodeEventEmitter, anyEvent, mapEvent, debounceEvent } from 'vs/base/common/event';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { rimraf } from 'vs/base/node/pfs';
|
||||
|
||||
export class ExtensionsLifecycle extends Disposable {
|
||||
|
||||
private processesLimiter: Limiter<void> = new Limiter(5); // Run max 5 processes in parallel
|
||||
|
||||
constructor(
|
||||
@ILogService private logService: ILogService
|
||||
private environmentService: IEnvironmentService,
|
||||
private logService: ILogService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension): TPromise<void> {
|
||||
const uninstallScript = this.parseUninstallScript(extension);
|
||||
if (uninstallScript) {
|
||||
this.logService.info(extension.identifier.id, 'Running Uninstall hook');
|
||||
return this.processesLimiter.queue(() =>
|
||||
this.runUninstallHook(uninstallScript.uninstallHook, uninstallScript.args, extension)
|
||||
.then(() => this.logService.info(extension.identifier.id, 'Finished running uninstall hook'), err => this.logService.error(extension.identifier.id, `Failed to run uninstall hook: ${err}`)));
|
||||
async postUninstall(extension: ILocalExtension): Promise<void> {
|
||||
const script = this.parseScript(extension, 'uninstall');
|
||||
if (script) {
|
||||
this.logService.info(extension.identifier.id, `Running post uninstall script`);
|
||||
await this.processesLimiter.queue(() =>
|
||||
this.runLifecycleHook(script.script, 'uninstall', script.args, true, extension)
|
||||
.then(() => this.logService.info(extension.identifier.id, `Finished running post uninstall script`), err => this.logService.error(extension.identifier.id, `Failed to run post uninstall script: ${err}`)));
|
||||
}
|
||||
return TPromise.as(null);
|
||||
return rimraf(this.getExtensionStoragePath(extension)).then(null, e => this.logService.error('Error while removing extension storage path', e));
|
||||
}
|
||||
|
||||
private parseUninstallScript(extension: ILocalExtension): { uninstallHook: string, args: string[] } {
|
||||
if (extension.location.scheme === Schemas.file && extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts']['vscode:uninstall'] === 'string') {
|
||||
const uninstallScript = (<string>extension.manifest['scripts']['vscode:uninstall']).split(' ');
|
||||
if (uninstallScript.length < 2 || uninstallScript[0] !== 'node' || !uninstallScript[1]) {
|
||||
this.logService.warn(extension.identifier.id, 'Uninstall script should be a node script');
|
||||
private parseScript(extension: ILocalExtension, type: string): { script: string, args: string[] } | null {
|
||||
const scriptKey = `vscode:${type}`;
|
||||
if (extension.location.scheme === Schemas.file && extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts'][scriptKey] === 'string') {
|
||||
const script = (<string>extension.manifest['scripts'][scriptKey]).split(' ');
|
||||
if (script.length < 2 || script[0] !== 'node' || !script[1]) {
|
||||
this.logService.warn(extension.identifier.id, `${scriptKey} should be a node script`);
|
||||
return null;
|
||||
}
|
||||
return { uninstallHook: posix.join(extension.location.fsPath, uninstallScript[1]), args: uninstallScript.slice(2) || [] };
|
||||
return { script: posix.join(extension.location.fsPath, script[1]), args: script.slice(2) || [] };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private runUninstallHook(lifecycleHook: string, args: string[], extension: ILocalExtension): TPromise<void> {
|
||||
return new TPromise((c, e) => {
|
||||
private runLifecycleHook(lifecycleHook: string, lifecycleType: string, args: string[], timeout: boolean, extension: ILocalExtension): Thenable<void> {
|
||||
return new Promise<void>((c, e) => {
|
||||
|
||||
const extensionLifecycleProcess = this.start(lifecycleHook, args, extension);
|
||||
const extensionLifecycleProcess = this.start(lifecycleHook, lifecycleType, args, extension);
|
||||
let timeoutHandler;
|
||||
|
||||
const onexit = (error?: string) => {
|
||||
clearTimeout(timeoutHandler);
|
||||
timeoutHandler = null;
|
||||
if (timeoutHandler) {
|
||||
clearTimeout(timeoutHandler);
|
||||
timeoutHandler = null;
|
||||
}
|
||||
if (error) {
|
||||
e(error);
|
||||
} else {
|
||||
c(null);
|
||||
c(void 0);
|
||||
}
|
||||
};
|
||||
|
||||
// on error
|
||||
extensionLifecycleProcess.on('error', (err) => {
|
||||
if (timeoutHandler) {
|
||||
onexit(toErrorMessage(err) || 'Unknown');
|
||||
}
|
||||
onexit(toErrorMessage(err) || 'Unknown');
|
||||
});
|
||||
|
||||
// on exit
|
||||
extensionLifecycleProcess.on('exit', (code: number, signal: string) => {
|
||||
if (timeoutHandler) {
|
||||
onexit(code ? `Process exited with code ${code}` : void 0);
|
||||
}
|
||||
onexit(code ? `post-${lifecycleType} process exited with code ${code}` : void 0);
|
||||
});
|
||||
|
||||
// timeout: kill process after waiting for 5s
|
||||
timeoutHandler = setTimeout(() => {
|
||||
timeoutHandler = null;
|
||||
extensionLifecycleProcess.kill();
|
||||
e('timed out');
|
||||
}, 5000);
|
||||
if (timeout) {
|
||||
// timeout: kill process after waiting for 5s
|
||||
timeoutHandler = setTimeout(() => {
|
||||
timeoutHandler = null;
|
||||
extensionLifecycleProcess.kill();
|
||||
e('timed out');
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private start(uninstallHook: string, args: string[], extension: ILocalExtension): ChildProcess {
|
||||
private start(uninstallHook: string, lifecycleType: string, args: string[], extension: ILocalExtension): ChildProcess {
|
||||
const opts = {
|
||||
silent: true,
|
||||
execArgv: <string[]>undefined
|
||||
execArgv: undefined
|
||||
};
|
||||
const extensionUninstallProcess = fork(uninstallHook, ['--type=extensionUninstall', ...args], opts);
|
||||
const extensionUninstallProcess = fork(uninstallHook, [`--type=extension-post-${lifecycleType}`, ...args], opts);
|
||||
|
||||
// Catch all output coming from the process
|
||||
type Output = { data: string, format: string[] };
|
||||
extensionUninstallProcess.stdout.setEncoding('utf8');
|
||||
extensionUninstallProcess.stderr.setEncoding('utf8');
|
||||
|
||||
const onStdout = fromNodeEventEmitter<string>(extensionUninstallProcess.stdout, 'data');
|
||||
const onStderr = fromNodeEventEmitter<string>(extensionUninstallProcess.stderr, 'data');
|
||||
|
||||
// Log output
|
||||
onStdout(data => this.logService.info(extension.identifier.id, `post-${lifecycleType}`, data));
|
||||
onStderr(data => this.logService.error(extension.identifier.id, `post-${lifecycleType}`, data));
|
||||
|
||||
const onOutput = anyEvent(
|
||||
mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })),
|
||||
mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] }))
|
||||
);
|
||||
|
||||
// Debounce all output, so we can render it in the Chrome console as a group
|
||||
const onDebouncedOutput = debounceEvent<Output>(onOutput, (r, o) => {
|
||||
return r
|
||||
@@ -112,7 +119,7 @@ export class ExtensionsLifecycle extends Disposable {
|
||||
: { data: o.data, format: o.format };
|
||||
}, 100);
|
||||
|
||||
// Print out extension host output
|
||||
// Print out output
|
||||
onDebouncedOutput(data => {
|
||||
console.group(extension.identifier.id);
|
||||
console.log(data.data, ...data.format);
|
||||
@@ -121,4 +128,8 @@ export class ExtensionsLifecycle extends Disposable {
|
||||
|
||||
return extensionUninstallProcess;
|
||||
}
|
||||
|
||||
private getExtensionStoragePath(extension: ILocalExtension): string {
|
||||
return posix.join(this.environmentService.globalStorageHome, extension.identifier.id.toLocaleLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement';
|
||||
import { Event, buffer, mapEvent } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IURITransformer } from 'vs/base/common/uriIpc';
|
||||
|
||||
export class ExtensionManagementChannel implements IServerChannel {
|
||||
|
||||
onInstallExtension: Event<InstallExtensionEvent>;
|
||||
onDidInstallExtension: Event<DidInstallExtensionEvent>;
|
||||
onUninstallExtension: Event<IExtensionIdentifier>;
|
||||
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
|
||||
|
||||
constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer) {
|
||||
this.onInstallExtension = buffer(service.onInstallExtension, true);
|
||||
this.onDidInstallExtension = buffer(service.onDidInstallExtension, true);
|
||||
this.onUninstallExtension = buffer(service.onUninstallExtension, true);
|
||||
this.onDidUninstallExtension = buffer(service.onDidUninstallExtension, true);
|
||||
}
|
||||
|
||||
listen(context, event: string): Event<any> {
|
||||
const uriTransformer = this.getUriTransformer(context);
|
||||
switch (event) {
|
||||
case 'onInstallExtension': return this.onInstallExtension;
|
||||
case 'onDidInstallExtension': return mapEvent(this.onDidInstallExtension, i => ({ ...i, local: this._transformOutgoing(i.local, uriTransformer) }));
|
||||
case 'onUninstallExtension': return this.onUninstallExtension;
|
||||
case 'onDidUninstallExtension': return this.onDidUninstallExtension;
|
||||
}
|
||||
|
||||
throw new Error('Invalid listen');
|
||||
}
|
||||
|
||||
call(context, command: string, args?: any): Thenable<any> {
|
||||
const uriTransformer = this.getUriTransformer(context);
|
||||
switch (command) {
|
||||
case 'zip': return this.service.zip(this._transformIncoming(args[0], uriTransformer)).then(uri => uriTransformer.transformOutgoing(uri));
|
||||
case 'unzip': return this.service.unzip(URI.revive(uriTransformer.transformIncoming(args[0])), args[1]);
|
||||
case 'install': return this.service.install(URI.revive(uriTransformer.transformIncoming(args[0])));
|
||||
case 'installFromGallery': return this.service.installFromGallery(args[0]);
|
||||
case 'uninstall': return this.service.uninstall(this._transformIncoming(args[0], uriTransformer), args[1]);
|
||||
case 'reinstallFromGallery': return this.service.reinstallFromGallery(this._transformIncoming(args[0], uriTransformer));
|
||||
case 'getInstalled': return this.service.getInstalled(args[0]).then(extensions => extensions.map(e => this._transformOutgoing(e, uriTransformer)));
|
||||
case 'updateMetadata': return this.service.updateMetadata(this._transformIncoming(args[0], uriTransformer), args[1]).then(e => this._transformOutgoing(e, uriTransformer));
|
||||
case 'getExtensionsReport': return this.service.getExtensionsReport();
|
||||
}
|
||||
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
|
||||
private _transformIncoming(extension: ILocalExtension, uriTransformer: IURITransformer): ILocalExtension {
|
||||
return extension ? { ...extension, ...{ location: URI.revive(uriTransformer.transformIncoming(extension.location)) } } : extension;
|
||||
}
|
||||
|
||||
private _transformOutgoing(extension: ILocalExtension, uriTransformer: IURITransformer): ILocalExtension;
|
||||
private _transformOutgoing(extension: ILocalExtension | undefined, uriTransformer: IURITransformer): ILocalExtension | undefined;
|
||||
private _transformOutgoing(extension: ILocalExtension | undefined, uriTransformer: IURITransformer): ILocalExtension | undefined {
|
||||
return extension ? { ...extension, ...{ location: uriTransformer.transformOutgoing(extension.location) } } : extension;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionManagementChannelClient implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
constructor(private channel: IChannel) { }
|
||||
|
||||
get onInstallExtension(): Event<InstallExtensionEvent> { return this.channel.listen('onInstallExtension'); }
|
||||
get onDidInstallExtension(): Event<DidInstallExtensionEvent> { return mapEvent(this.channel.listen<DidInstallExtensionEvent>('onDidInstallExtension'), i => ({ ...i, local: this._transformIncoming(i.local) })); }
|
||||
get onUninstallExtension(): Event<IExtensionIdentifier> { return this.channel.listen('onUninstallExtension'); }
|
||||
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this.channel.listen('onDidUninstallExtension'); }
|
||||
|
||||
zip(extension: ILocalExtension): Promise<URI> {
|
||||
return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(result)));
|
||||
}
|
||||
|
||||
unzip(zipLocation: URI, type: LocalExtensionType): Promise<IExtensionIdentifier> {
|
||||
return Promise.resolve(this.channel.call('unzip', [zipLocation, type]));
|
||||
}
|
||||
|
||||
install(vsix: URI): Promise<IExtensionIdentifier> {
|
||||
return Promise.resolve(this.channel.call('install', [vsix]));
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): Promise<void> {
|
||||
return Promise.resolve(this.channel.call('installFromGallery', [extension]));
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension, force = false): Promise<void> {
|
||||
return Promise.resolve(this.channel.call('uninstall', [extension!, force]));
|
||||
}
|
||||
|
||||
reinstallFromGallery(extension: ILocalExtension): Promise<void> {
|
||||
return Promise.resolve(this.channel.call('reinstallFromGallery', [extension]));
|
||||
}
|
||||
|
||||
getInstalled(type: LocalExtensionType | null = null): Promise<ILocalExtension[]> {
|
||||
return Promise.resolve(this.channel.call<ILocalExtension[]>('getInstalled', [type]))
|
||||
.then(extensions => extensions.map(extension => this._transformIncoming(extension)));
|
||||
}
|
||||
|
||||
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
|
||||
return Promise.resolve(this.channel.call<ILocalExtension>('updateMetadata', [local, metadata]))
|
||||
.then(extension => this._transformIncoming(extension));
|
||||
}
|
||||
|
||||
getExtensionsReport(): Promise<IReportedExtension[]> {
|
||||
return Promise.resolve(this.channel.call('getExtensionsReport'));
|
||||
}
|
||||
|
||||
private _transformIncoming(extension: ILocalExtension): ILocalExtension;
|
||||
private _transformIncoming(extension: ILocalExtension | undefined): ILocalExtension | undefined;
|
||||
private _transformIncoming(extension: ILocalExtension | undefined): ILocalExtension | undefined {
|
||||
return extension ? { ...extension, ...{ location: URI.revive(extension.location) } } : extension;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,12 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as semver from 'semver';
|
||||
import { adoptToGalleryExtensionId, LOCAL_EXTENSION_ID_REGEX } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { buffer } from 'vs/platform/node/zip';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export function getIdAndVersionFromLocalExtensionId(localExtensionId: string): { id: string, version: string } {
|
||||
export function getIdAndVersionFromLocalExtensionId(localExtensionId: string): { id: string, version: string | null } {
|
||||
const matches = LOCAL_EXTENSION_ID_REGEX.exec(localExtensionId);
|
||||
if (matches && matches[1] && matches[2]) {
|
||||
const version = semver.valid(matches[2]);
|
||||
@@ -20,4 +21,15 @@ export function getIdAndVersionFromLocalExtensionId(localExtensionId: string): {
|
||||
id: adoptToGalleryExtensionId(localExtensionId),
|
||||
version: null
|
||||
};
|
||||
}
|
||||
|
||||
export function getManifest(vsix: string): Promise<IExtensionManifest> {
|
||||
return buffer(vsix, 'extension/package.json')
|
||||
.then(buffer => {
|
||||
try {
|
||||
return JSON.parse(buffer.toString('utf8'));
|
||||
} catch (err) {
|
||||
throw new Error(localize('invalidManifest', "VSIX invalid: package.json is not a JSON file."));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { join } from 'path';
|
||||
@@ -37,6 +36,6 @@ export class ExtensionsManifestCache extends Disposable {
|
||||
}
|
||||
|
||||
invalidate(): void {
|
||||
pfs.del(this.extensionsManifestCache).done(() => { }, () => { });
|
||||
pfs.del(this.extensionsManifestCache).then(() => { }, () => { });
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,113 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, EventMultiplexer } from 'vs/base/common/event';
|
||||
import {
|
||||
IExtensionManagementService, ILocalExtension, IGalleryExtension, LocalExtensionType, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata,
|
||||
IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { isUIExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IRemoteAuthorityResolverService, ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
|
||||
|
||||
export class MulitExtensionManagementService extends Disposable implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
readonly onInstallExtension: Event<InstallExtensionEvent>;
|
||||
readonly onDidInstallExtension: Event<DidInstallExtensionEvent>;
|
||||
readonly onUninstallExtension: Event<IExtensionIdentifier>;
|
||||
readonly onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
|
||||
|
||||
private readonly servers: IExtensionManagementServer[];
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService,
|
||||
@IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IRemoteAuthorityResolverService private remoteAuthorityResolverService: IRemoteAuthorityResolverService
|
||||
) {
|
||||
super();
|
||||
this.servers = this.extensionManagementServerService.remoteExtensionManagementServer ? [this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer] : [this.extensionManagementServerService.localExtensionManagementServer];
|
||||
|
||||
this.onInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<InstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer<InstallExtensionEvent>())).event;
|
||||
this.onDidInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<DidInstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer<DidInstallExtensionEvent>())).event;
|
||||
this.onUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<IExtensionIdentifier>, server) => { emitter.add(server.extensionManagementService.onUninstallExtension); return emitter; }, new EventMultiplexer<IExtensionIdentifier>())).event;
|
||||
this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<DidUninstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer<DidUninstallExtensionEvent>())).event;
|
||||
}
|
||||
|
||||
getInstalled(type?: LocalExtensionType): Promise<ILocalExtension[]> {
|
||||
return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type)))
|
||||
.then(result => flatten(result));
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension, force?: boolean): Promise<void> {
|
||||
return this.getServer(extension).extensionManagementService.uninstall(extension, force);
|
||||
}
|
||||
|
||||
reinstallFromGallery(extension: ILocalExtension): Promise<void> {
|
||||
return this.getServer(extension).extensionManagementService.reinstallFromGallery(extension);
|
||||
}
|
||||
|
||||
updateMetadata(extension: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
|
||||
return this.getServer(extension).extensionManagementService.updateMetadata(extension, metadata);
|
||||
}
|
||||
|
||||
zip(extension: ILocalExtension): Promise<URI> {
|
||||
throw new Error('Not Supported');
|
||||
}
|
||||
|
||||
unzip(zipLocation: URI, type: LocalExtensionType): Promise<IExtensionIdentifier> {
|
||||
return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(() => null);
|
||||
}
|
||||
|
||||
install(vsix: URI): Promise<IExtensionIdentifier> {
|
||||
if (!this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix);
|
||||
}
|
||||
return Promise.all([getManifest(vsix.fsPath), this.hasToSyncExtensions()])
|
||||
.then(([manifest, syncExtensions]) => {
|
||||
const servers = isUIExtension(manifest, this.configurationService) ? [this.extensionManagementServerService.localExtensionManagementServer] : syncExtensions ? this.servers : [this.extensionManagementServerService.remoteExtensionManagementServer];
|
||||
return Promise.all(servers.map(server => server.extensionManagementService.install(vsix)))
|
||||
.then(() => null);
|
||||
});
|
||||
}
|
||||
|
||||
installFromGallery(gallery: IGalleryExtension): Promise<void> {
|
||||
if (!this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
|
||||
}
|
||||
return Promise.all([this.extensionGalleryService.getManifest(gallery, CancellationToken.None), this.hasToSyncExtensions()])
|
||||
.then(([manifest, syncExtensions]) => {
|
||||
const servers = isUIExtension(manifest, this.configurationService) ? [this.extensionManagementServerService.localExtensionManagementServer] : syncExtensions ? this.servers : [this.extensionManagementServerService.remoteExtensionManagementServer];
|
||||
return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery)))
|
||||
.then(() => null);
|
||||
});
|
||||
}
|
||||
|
||||
getExtensionsReport(): Promise<IReportedExtension[]> {
|
||||
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport();
|
||||
}
|
||||
|
||||
private getServer(extension: ILocalExtension): IExtensionManagementServer {
|
||||
return this.extensionManagementServerService.getExtensionManagementServer(extension.location);
|
||||
}
|
||||
|
||||
private _remoteAuthorityResolverPromise: Thenable<ResolvedAuthority>;
|
||||
private hasToSyncExtensions(): Thenable<boolean> {
|
||||
if (!this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
if (!this._remoteAuthorityResolverPromise) {
|
||||
this._remoteAuthorityResolverPromise = this.remoteAuthorityResolverService.resolveAuthority(this.extensionManagementServerService.remoteExtensionManagementServer.authority);
|
||||
}
|
||||
return this._remoteAuthorityResolverPromise.then(({ syncExtensions }) => !!syncExtensions);
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,16 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as sinon from 'sinon';
|
||||
import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension, LocalExtensionType, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
|
||||
function storageService(instantiationService: TestInstantiationService): IStorageService {
|
||||
let service = instantiationService.get(IStorageService);
|
||||
@@ -25,18 +22,17 @@ function storageService(instantiationService: TestInstantiationService): IStorag
|
||||
getWorkbenchState: () => WorkbenchState.FOLDER,
|
||||
});
|
||||
}
|
||||
service = instantiationService.stub(IStorageService, instantiationService.createInstance(StorageService, new InMemoryLocalStorage(), new InMemoryLocalStorage()));
|
||||
service = instantiationService.stub(IStorageService, new TestStorageService());
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
export class TestExtensionEnablementService extends ExtensionEnablementService {
|
||||
constructor(instantiationService: TestInstantiationService) {
|
||||
super(storageService(instantiationService), instantiationService.get(IWorkspaceContextService),
|
||||
instantiationService.get(IEnvironmentService) || instantiationService.stub(IEnvironmentService, {} as IEnvironmentService),
|
||||
instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService,
|
||||
{ onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event } as IExtensionManagementService));
|
||||
{ onDidInstallExtension: new Emitter<DidInstallExtensionEvent>().event, onDidUninstallExtension: new Emitter<DidUninstallExtensionEvent>().event } as IExtensionManagementService));
|
||||
}
|
||||
|
||||
public async reset(): Promise<void> {
|
||||
@@ -50,10 +46,11 @@ suite('ExtensionEnablementService Test', () => {
|
||||
let testObject: IExtensionEnablementService;
|
||||
|
||||
const didUninstallEvent: Emitter<DidUninstallExtensionEvent> = new Emitter<DidUninstallExtensionEvent>();
|
||||
const didInstallEvent: Emitter<DidInstallExtensionEvent> = new Emitter<DidInstallExtensionEvent>();
|
||||
|
||||
setup(() => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => TPromise.as([]) } as IExtensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, onDidInstallExtension: didInstallEvent.event, getInstalled: () => Promise.resolve([]) } as IExtensionManagementService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
});
|
||||
|
||||
@@ -298,6 +295,90 @@ suite('ExtensionEnablementService Test', () => {
|
||||
.then(extensions => assert.deepEqual([], extensions));
|
||||
});
|
||||
|
||||
test('test installing an extension re-eanbles it when disabled globally', async () => {
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.Disabled);
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install });
|
||||
const extensions = await testObject.getDisabledExtensions();
|
||||
assert.deepEqual([], extensions);
|
||||
});
|
||||
|
||||
test('test updating an extension does not re-eanbles it when disabled globally', async () => {
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.Disabled);
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update });
|
||||
const extensions = await testObject.getDisabledExtensions();
|
||||
assert.deepEqual([{ id: 'pub.a' }], extensions);
|
||||
});
|
||||
|
||||
test('test installing an extension fires enablement change event when disabled globally', async () => {
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.Disabled);
|
||||
return new Promise((c, e) => {
|
||||
testObject.onEnablementChanged(e => {
|
||||
if (e.id === local.galleryIdentifier.id) {
|
||||
c();
|
||||
}
|
||||
});
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install });
|
||||
});
|
||||
});
|
||||
|
||||
test('test updating an extension does not fires enablement change event when disabled globally', async () => {
|
||||
const target = sinon.spy();
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.Disabled);
|
||||
testObject.onEnablementChanged(target);
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update });
|
||||
assert.ok(!target.called);
|
||||
});
|
||||
|
||||
test('test installing an extension re-eanbles it when workspace disabled', async () => {
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.WorkspaceDisabled);
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install });
|
||||
const extensions = await testObject.getDisabledExtensions();
|
||||
assert.deepEqual([], extensions);
|
||||
});
|
||||
|
||||
test('test updating an extension does not re-eanbles it when workspace disabled', async () => {
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.WorkspaceDisabled);
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update });
|
||||
const extensions = await testObject.getDisabledExtensions();
|
||||
assert.deepEqual([{ id: 'pub.a' }], extensions);
|
||||
});
|
||||
|
||||
test('test installing an extension fires enablement change event when workspace disabled', async () => {
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.WorkspaceDisabled);
|
||||
return new Promise((c, e) => {
|
||||
testObject.onEnablementChanged(e => {
|
||||
if (e.id === local.galleryIdentifier.id) {
|
||||
c();
|
||||
}
|
||||
});
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install });
|
||||
});
|
||||
});
|
||||
|
||||
test('test updating an extension does not fires enablement change event when workspace disabled', async () => {
|
||||
const target = sinon.spy();
|
||||
const local = aLocalExtension('pub.a');
|
||||
await testObject.setEnablement(local, EnablementState.WorkspaceDisabled);
|
||||
testObject.onEnablementChanged(target);
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update });
|
||||
assert.ok(!target.called);
|
||||
});
|
||||
|
||||
test('test installing an extension should not fire enablement change event when extension is not disabled', async () => {
|
||||
const target = sinon.spy();
|
||||
const local = aLocalExtension('pub.a');
|
||||
testObject.onEnablementChanged(target);
|
||||
didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install });
|
||||
assert.ok(!target.called);
|
||||
});
|
||||
|
||||
test('test remove an extension from disablement list when uninstalled', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled))
|
||||
@@ -356,7 +437,7 @@ suite('ExtensionEnablementService Test', () => {
|
||||
|
||||
test('test getDisabledExtensions include extensions disabled in enviroment', () => {
|
||||
instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService);
|
||||
instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => TPromise.as([aLocalExtension('pub.a'), aLocalExtension('pub.b')]) } as IExtensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, onDidInstallExtension: didInstallEvent.event, getInstalled: () => Promise.resolve([aLocalExtension('pub.a'), aLocalExtension('pub.b')]) } as IExtensionManagementService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
return testObject.getDisabledExtensions()
|
||||
.then(actual => {
|
||||
@@ -364,6 +445,7 @@ suite('ExtensionEnablementService Test', () => {
|
||||
assert.equal(actual[0].id, 'pub.a');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function aLocalExtension(id: string, contributes?: IExtensionContributions): ILocalExtension {
|
||||
@@ -2,8 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as os from 'os';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
|
||||
Reference in New Issue
Block a user