Task contribution (#742)

* work in progress

* set up necessary code. need to work on getting it working

* formatting

* work in progress

* work in progress

* formatting

* work in progress

* work in progress

* work in progress

* formatting

* needs a lot of work regarding how we do actions vs how extensions do actions

* formatting

* use connection profile for actions

* change action to be
This commit is contained in:
Anthony Dresser
2018-02-27 11:40:13 -08:00
committed by GitHub
parent 5adab4fafb
commit 3432dac261
23 changed files with 518 additions and 323 deletions

View File

@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* 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 { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { TaskRegistry, ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import {
ExtHostAccountManagementShape,
MainThreadAccountManagementShape,
SqlExtHostContext,
SqlMainContext,
ExtHostTasksShape,
MainThreadTasksShape
} from 'sql/workbench/api/node/sqlExtHost.protocol';
import { IConnectionProfile } from 'sqlops';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
@extHostNamedCustomer(SqlMainContext.MainThreadTasks)
export class MainThreadTasks implements MainThreadTasksShape {
private readonly _disposables = new Map<string, IDisposable>();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostTasksShape;
constructor(
extHostContext: IExtHostContext
) {
this._proxy = extHostContext.get(SqlExtHostContext.ExtHostTasks);
}
dispose() {
this._disposables.forEach(value => value.dispose());
this._disposables.clear();
this._generateCommandsDocumentationRegistration.dispose();
}
$registerTask(id: string): TPromise<any> {
this._disposables.set(
id,
TaskRegistry.registerTask(id, (accessor, profile: IConnectionProfile, ...args) => {
if (profile instanceof ConnectionProfile) {
profile = profile.toIConnectionProfile();
}
this._proxy.$executeContributedTask(id, profile, ...args);
})
);
return undefined;
}
$unregisterTask(id: string): TPromise<any> {
if (this._disposables.has(id)) {
this._disposables.get(id).dispose();
this._disposables.delete(id);
}
return undefined;
}
}

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* 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 { validateConstraint } from 'vs/base/common/types';
import { ILogService } from 'vs/platform/log/common/log';
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import { TPromise } from 'vs/base/common/winjs.base';
import * as sqlops from 'sqlops';
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
import { SqlMainContext, MainThreadTasksShape, ExtHostTasksShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
interface TaskHandler {
callback: Function;
thisArg: any;
description: ITaskHandlerDescription;
}
export class ExtHostTasks implements ExtHostTasksShape {
private _proxy: MainThreadTasksShape;
private _tasks = new Map<string, TaskHandler>();
constructor(
mainContext: IMainContext,
private logService: ILogService
) {
this._proxy = mainContext.get(SqlMainContext.MainThreadTasks);
}
registerTask(id: string, callback: sqlops.tasks.ITaskHandler, thisArg?: any, description?: ITaskHandlerDescription): extHostTypes.Disposable {
this.logService.trace('ExtHostTasks#registerTask', id);
if (!id.trim().length) {
throw new Error('invalid id');
}
if (this._tasks.has(id)) {
throw new Error(`task '${id}' already exists`);
}
this._tasks.set(id, { callback, thisArg, description });
this._proxy.$registerTask(id);
return new extHostTypes.Disposable(() => {
if (this._tasks.delete(id)) {
this._proxy.$unregisterTask(id);
}
});
}
$executeContributedTask<T>(id: string, ...args: any[]): Thenable<T> {
let command = this._tasks.get(id);
if (!command) {
return TPromise.wrapError<T>(new Error(`Contributed task '${id}' does not exist.`));
}
let { callback, thisArg, description } = command;
if (description) {
for (let i = 0; i < description.args.length; i++) {
try {
validateConstraint(args[i], description.args[i].constraint);
} catch (err) {
return TPromise.wrapError<T>(new Error(`Running the contributed task:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`));
}
}
}
try {
let result = callback.apply(thisArg, args);
return TPromise.as(result);
} catch (err) {
// console.log(err);
// try {
// console.log(toErrorMessage(err));
// } catch (err) {
// //
// }
return TPromise.wrapError<T>(new Error(`Running the contributed task:'${id}' failed.`));
}
}
$getContributedTaskHandlerDescriptions(): TPromise<{ [id: string]: any; }> {
throw new Error('Method not implemented.');
}
}

View File

@@ -26,6 +26,7 @@ import * as sqlExtHostTypes from 'sql/workbench/api/common/sqlExtHostTypes';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostModalDialogs } from 'sql/workbench/api/node/extHostModalDialog';
import { ExtHostTasks } from 'sql/workbench/api/node/extHostTasks';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { ExtHostDashboardWebviews } from 'sql/workbench/api/node/extHostDashboardWebview';
@@ -57,6 +58,7 @@ export function createApiFactory(
const extHostSerializationProvider = threadService.set(SqlExtHostContext.ExtHostSerializationProvider, new ExtHostSerializationProvider(threadService));
const extHostResourceProvider = threadService.set(SqlExtHostContext.ExtHostResourceProvider, new ExtHostResourceProvider(threadService));
const extHostModalDialogs = threadService.set(SqlExtHostContext.ExtHostModalDialogs, new ExtHostModalDialogs(threadService));
const extHostTasks = threadService.set(SqlExtHostContext.ExtHostTasks, new ExtHostTasks(threadService, logService));
const extHostWebviewWidgets = threadService.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(threadService));
return {
@@ -255,12 +257,18 @@ export function createApiFactory(
}
};
const window = {
const window: typeof sqlops.window = {
createDialog(name: string) {
return extHostModalDialogs.createDialog(name);
}
};
const tasks: typeof sqlops.tasks = {
registerTask(id: string, task: (...args: any[]) => any, thisArgs?: any): vscode.Disposable {
return extHostTasks.registerTask(id, task, thisArgs);
}
};
const dashboard = {
registerWebviewProvider(widgetId: string, handler: (webview: sqlops.DashboardWebview) => void) {
extHostWebviewWidgets.$registerProvider(widgetId, handler);
@@ -282,6 +290,7 @@ export function createApiFactory(
TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode,
ScriptOperation: sqlExtHostTypes.ScriptOperation,
window,
tasks,
dashboard
};
}

View File

@@ -15,6 +15,7 @@ import 'sql/workbench/api/node/mainThreadCredentialManagement';
import 'sql/workbench/api/node/mainThreadDataProtocol';
import 'sql/workbench/api/node/mainThreadSerializationProvider';
import 'sql/workbench/api/node/mainThreadResourceProvider';
import 'sql/workbench/api/electron-browser/mainThreadTasks';
import 'sql/workbench/api/node/mainThreadDashboardWebview';
import './mainThreadAccountManagement';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';

View File

@@ -9,11 +9,12 @@ import {
createExtHostContextProxyIdentifier as createExtId,
ProxyIdentifier
} from 'vs/workbench/services/thread/common/threadService';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as sqlops from 'sqlops';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
export abstract class ExtHostAccountManagementShape {
$autoOAuthCancelled(handle: number): Thenable<void> { throw ni(); }
@@ -421,6 +422,7 @@ export const SqlMainContext = {
MainThreadSerializationProvider: createMainId<MainThreadSerializationProviderShape>('MainThreadSerializationProvider'),
MainThreadResourceProvider: createMainId<MainThreadResourceProviderShape>('MainThreadResourceProvider'),
MainThreadModalDialog: createMainId<MainThreadModalDialogShape>('MainThreadModalDialog'),
MainThreadTasks: createMainId<MainThreadTasksShape>('MainThreadTasks'),
MainThreadDashboardWebview: createMainId<MainThreadDashboardWebviewShape>('MainThreadDashboardWebview')
};
@@ -432,6 +434,7 @@ export const SqlExtHostContext = {
ExtHostSerializationProvider: createExtId<ExtHostSerializationProviderShape>('ExtHostSerializationProvider'),
ExtHostResourceProvider: createExtId<ExtHostResourceProviderShape>('ExtHostResourceProvider'),
ExtHostModalDialogs: createExtId<ExtHostModalDialogsShape>('ExtHostModalDialogs'),
ExtHostTasks: createExtId<ExtHostTasksShape>('ExtHostTasks'),
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews')
};
@@ -449,6 +452,16 @@ export interface ExtHostModalDialogsShape {
$onClosed(handle: number): void;
}
export interface ExtHostTasksShape {
$executeContributedTask<T>(id: string, ...args: any[]): Thenable<T>;
$getContributedTaskHandlerDescriptions(): TPromise<{ [id: string]: string | ITaskHandlerDescription }>;
}
export interface MainThreadTasksShape extends IDisposable {
$registerTask(id: string): TPromise<any>;
$unregisterTask(id: string): TPromise<any>;
}
export interface ExtHostDashboardWebviewsShape {
$registerProvider(widgetId: string, handler: (webview: sqlops.DashboardWebview) => void): void;
$onMessage(handle: number, message: any): void;

View File

@@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerTask } from 'sql/platform/tasks/taskRegistry';
import { TaskRegistry } from 'sql/platform/tasks/common/tasks';
import * as Actions from './actions';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
@@ -15,39 +15,14 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr
import { ShowCurrentReleaseNotesAction, ProductContribution } from 'sql/workbench/update/releaseNotes';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
const backupSchema: IJSONSchema = {
description: nls.localize('carbon.actions.back', 'Open up backup dialog'),
type: 'null',
default: null
};
const restoreSchema: IJSONSchema = {
description: nls.localize('carbon.actions.restore', 'Open up restore dialog'),
type: 'null',
default: null
};
const newQuerySchema: IJSONSchema = {
description: nls.localize('carbon.actions.newQuery', 'Open a new query window'),
type: 'null',
default: null
};
const configureDashboardSchema: IJSONSchema = {
description: nls.localize('carbon.actions.configureDashboard', 'Configure the Management Dashboard'),
type: 'null',
default: null
};
registerTask('backup', '', backupSchema, Actions.BackupAction);
registerTask('restore', '', restoreSchema, Actions.RestoreAction);
registerTask('new-query', '', newQuerySchema, Actions.NewQueryAction);
registerTask('configure-dashboard', '', configureDashboardSchema, Actions.ConfigureDashboardAction);
new Actions.BackupAction().registerTask();
new Actions.RestoreAction().registerTask();
new Actions.NewQueryAction().registerTask();
new Actions.ConfigureDashboardAction().registerTask();
// add product update and release notes contributions
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Running);
.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Running);
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Getting Started');

View File

@@ -15,17 +15,18 @@ import { IAngularEventingService, AngularEventType } from 'sql/services/angularE
import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces';
import { IAdminService } from 'sql/parts/admin/common/adminService';
import * as Constants from 'sql/common/constants';
import { ObjectMetadata } from 'sqlops';
import { ScriptOperation } from 'sql/workbench/common/taskUtilities';
import { TaskAction } from 'sql/platform/tasks/taskRegistry';
import { Task } from 'sql/platform/tasks/common/tasks';
import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService';
import { ObjectMetadata } from 'sqlops';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import * as nls from 'vs/nls';
import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
export interface BaseActionContext {
object?: ObjectMetadata;
@@ -41,35 +42,29 @@ export interface ManageActionContext extends BaseActionContext {
}
// --- actions
export class NewQueryAction extends TaskAction {
export class NewQueryAction extends Task {
public static ID = 'newQuery';
public static LABEL = nls.localize('newQuery', 'New Query');
public static ICON = 'new-query';
constructor(
id: string, label: string, icon: string,
@IQueryEditorService protected _queryEditorService: IQueryEditorService,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@IObjectExplorerService protected _objectExplorerService: IObjectExplorerService,
@IWorkbenchEditorService protected _workbenchEditorService: IWorkbenchEditorService
) {
super(id, label, icon);
constructor() {
super({ id: NewQueryAction.ID, title: NewQueryAction.LABEL, iconClass: NewQueryAction.ICON });
}
public run(actionContext: BaseActionContext): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
public runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
TaskUtilities.newQuery(
actionContext.profile,
this._connectionManagementService,
this._queryEditorService,
this._objectExplorerService,
this._workbenchEditorService
profile,
accessor.get<IConnectionManagementService>(IConnectionManagementService),
accessor.get<IQueryEditorService>(IQueryEditorService),
accessor.get<IObjectExplorerService>(IObjectExplorerService),
accessor.get<IWorkbenchEditorService>(IWorkbenchEditorService)
).then(
result => {
resolve(true);
resolve(void 0);
},
error => {
resolve(false);
resolve(void 0);
}
);
});
@@ -284,58 +279,52 @@ export class ScriptDeleteAction extends Action {
}
}
export class BackupAction extends TaskAction {
public static ID = Constants.BackupFeatureName;
public static LABEL = nls.localize('backup', 'Backup');
public static ICON = Constants.BackupFeatureName;
export class BackupAction extends Task {
public static readonly ID = Constants.BackupFeatureName;
public static readonly LABEL = nls.localize('backup', 'Backup');
public static readonly ICON = Constants.BackupFeatureName;
constructor(
id: string, label: string, icon: string,
@IBackupUiService protected _backupUiService: IBackupUiService
) {
super(id, label, icon);
constructor() {
super({ id: BackupAction.ID, title: BackupAction.LABEL, iconClass: BackupAction.ICON });
}
run(actionContext: BaseActionContext): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
TaskUtilities.showBackup(
actionContext.profile,
this._backupUiService,
profile,
accessor.get<IBackupUiService>(IBackupUiService)
).then(
result => {
resolve(true);
resolve(void 0);
},
error => {
resolve(false);
resolve(void 0);
}
);
});
}
}
export class RestoreAction extends TaskAction {
public static ID = Constants.RestoreFeatureName;
public static LABEL = nls.localize('restore', 'Restore');
public static ICON = Constants.RestoreFeatureName;
export class RestoreAction extends Task {
public static readonly ID = Constants.RestoreFeatureName;
public static readonly LABEL = nls.localize('restore', 'Restore');
public static readonly ICON = Constants.RestoreFeatureName;
constructor(
id: string, label: string, icon: string,
@IRestoreDialogController protected _restoreService: IRestoreDialogController
) {
super(id, label, icon);
constructor() {
super({ id: RestoreAction.ID, title: RestoreAction.LABEL, iconClass: RestoreAction.ICON });
}
run(actionContext: BaseActionContext): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
TaskUtilities.showRestore(
actionContext.profile,
this._restoreService
profile,
accessor.get<IRestoreDialogController>(IRestoreDialogController)
).then(
result => {
resolve(true);
resolve(void 0);
},
error => {
resolve(false);
resolve(void 0);
}
);
});
@@ -390,7 +379,7 @@ export class InsightAction extends Action {
}
}
export class NewDatabaseAction extends TaskAction {
export class NewDatabaseAction extends Action {
public static ID = 'newDatabase';
public static LABEL = nls.localize('newDatabase', 'New Database');
public static ICON = 'new-database';
@@ -410,24 +399,22 @@ export class NewDatabaseAction extends TaskAction {
}
}
export class ConfigureDashboardAction extends TaskAction {
public static ID = 'configureDashboard';
public static LABEL = nls.localize('configureDashboard', 'Configure');
public static ICON = 'configure-dashboard';
export class ConfigureDashboardAction extends Task {
public static readonly ID = 'configureDashboard';
public static readonly LABEL = nls.localize('configureDashboard', 'Configure');
public static readonly ICON = 'configure-dashboard';
private static readonly configHelpUri = 'https://aka.ms/sqldashboardconfig';
constructor(
id: string, label: string, icon: string,
@IWindowsService private _windowsService
) {
super(id, label, icon);
constructor() {
super({ id: ConfigureDashboardAction.ID, title: ConfigureDashboardAction.LABEL, iconClass: ConfigureDashboardAction.ICON });
}
run(actionContext: BaseActionContext): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
this._windowsService.openExternal(ConfigureDashboardAction.configHelpUri).then((result) => {
resolve(result);
runTask(accessor: ServicesAccessor): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
accessor.get<IWindowsService>(IWindowsService).openExternal(ConfigureDashboardAction.configHelpUri).then((result) => {
resolve(void 0);
}, err => {
resolve(err);
resolve(void 0);
});
});
}