mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
data workspace extension batch 2 (#12208)
* work in progress * load projects in view and test cases * update scope * make the sql proj menu available in workspace view * add extension unit test * address comments * fix errors
This commit is contained in:
@@ -46,7 +46,7 @@ jobs:
|
||||
steps:
|
||||
- template: linux/sql-product-build-linux.yml
|
||||
parameters:
|
||||
extensionsToUnitTest: ["admin-tool-ext-win", "agent", "azdata", "azurecore", "cms", "dacpac", "import", "schema-compare", "notebook", "resource-deployment", "machine-learning", "sql-database-projects"]
|
||||
extensionsToUnitTest: ["admin-tool-ext-win", "agent", "azdata", "azurecore", "cms", "dacpac", "import", "schema-compare", "notebook", "resource-deployment", "machine-learning", "sql-database-projects", "data-workspace"]
|
||||
timeoutInMinutes: 70
|
||||
|
||||
- job: LinuxWeb
|
||||
|
||||
@@ -20,20 +20,41 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/azuredatastudio.git"
|
||||
},
|
||||
"extensionDependencies": [
|
||||
"microsoft.mssql"
|
||||
],
|
||||
"extensionDependencies": [],
|
||||
"contributes": {
|
||||
"configuration": [
|
||||
{
|
||||
"title": "Projects",
|
||||
"properties": {
|
||||
"dataworkspace.projects": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "projects.addProject",
|
||||
"title": "%add-project-command%",
|
||||
"category": "",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "dataworkspace.refresh",
|
||||
"title": "%refresh-workspace-command%",
|
||||
"category": "",
|
||||
"icon": "$(refresh)"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "dataworkspace.refresh",
|
||||
"when": "view == dataworkspace.views.main",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "projects.addProject",
|
||||
"when": "view == dataworkspace.views.main",
|
||||
@@ -44,6 +65,10 @@
|
||||
{
|
||||
"command": "projects.addProject",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "dataworkspace.refresh",
|
||||
"when": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
"extension-description": "Data workspace",
|
||||
"data-workspace-view-container-name": "Projects",
|
||||
"main-view-name": "Projects",
|
||||
"add-project-command": "Add Project"
|
||||
"add-project-command": "Add Project",
|
||||
"refresh-workspace-command": "Refresh"
|
||||
}
|
||||
|
||||
70
extensions/data-workspace/src/common/interfaces.ts
Normal file
70
extensions/data-workspace/src/common/interfaces.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IProjectProvider, IProjectType } from 'dataworkspace';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
/**
|
||||
* Defines the project provider registry
|
||||
*/
|
||||
export interface IProjectProviderRegistry {
|
||||
/**
|
||||
* Registers a new project provider
|
||||
* @param provider The project provider
|
||||
*/
|
||||
registerProvider(provider: IProjectProvider): vscode.Disposable;
|
||||
|
||||
/**
|
||||
* Clear the providers
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
* Gets all the registered providers
|
||||
*/
|
||||
readonly providers: IProjectProvider[];
|
||||
|
||||
/**
|
||||
* Gets the project provider for the specified project type
|
||||
* @param projectType The project type, file extension of the project
|
||||
*/
|
||||
getProviderByProjectType(projectType: string): IProjectProvider | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the project service
|
||||
*/
|
||||
export interface IWorkspaceService {
|
||||
/**
|
||||
* Gets all supported project types
|
||||
*/
|
||||
getAllProjectTypes(): Promise<IProjectType[]>;
|
||||
|
||||
/**
|
||||
* Gets the project files in current workspace
|
||||
*/
|
||||
getProjectsInWorkspace(): Promise<string[]>;
|
||||
|
||||
/**
|
||||
* Gets the project provider by project file
|
||||
* @param projectFilePath The full path of the project file
|
||||
*/
|
||||
getProjectProvider(projectFilePath: string): Promise<IProjectProvider | undefined>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the item for the workspace tree
|
||||
*/
|
||||
export interface WorkspaceTreeItem {
|
||||
/**
|
||||
* Gets the tree data provider
|
||||
*/
|
||||
treeDataProvider: vscode.TreeDataProvider<any>;
|
||||
|
||||
/**
|
||||
* Gets the raw element returned by the tree data provider
|
||||
*/
|
||||
element: any;
|
||||
}
|
||||
12
extensions/data-workspace/src/common/logger.ts
Normal file
12
extensions/data-workspace/src/common/logger.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export class Log {
|
||||
error(msg: string): void {
|
||||
console.error(msg);
|
||||
}
|
||||
}
|
||||
const Logger = new Log();
|
||||
export default Logger;
|
||||
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IProjectProvider } from 'dataworkspace';
|
||||
import * as vscode from 'vscode';
|
||||
import { IProjectProviderRegistry } from './interfaces';
|
||||
|
||||
export const ProjectProviderRegistry: IProjectProviderRegistry = new class implements IProjectProviderRegistry {
|
||||
private _providers = new Array<IProjectProvider>();
|
||||
private _providerMapping: { [key: string]: IProjectProvider } = {};
|
||||
|
||||
registerProvider(provider: IProjectProvider): vscode.Disposable {
|
||||
this.validateProvider(provider);
|
||||
this._providers.push(provider);
|
||||
provider.supportedProjectTypes.forEach(projectType => {
|
||||
this._providerMapping[projectType.projectFileExtension.toUpperCase()] = provider;
|
||||
});
|
||||
return new vscode.Disposable(() => {
|
||||
const idx = this._providers.indexOf(provider);
|
||||
if (idx >= 0) {
|
||||
this._providers.splice(idx, 1);
|
||||
provider.supportedProjectTypes.forEach(projectType => {
|
||||
delete this._providerMapping[projectType.projectFileExtension.toUpperCase()];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get providers(): IProjectProvider[] {
|
||||
return this._providers.slice(0);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._providers.length = 0;
|
||||
}
|
||||
|
||||
validateProvider(provider: IProjectProvider): void {
|
||||
}
|
||||
|
||||
getProviderByProjectType(projectType: string): IProjectProvider | undefined {
|
||||
return projectType ? this._providerMapping[projectType.toUpperCase()] : undefined;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { IWorkspaceService, WorkspaceTreeItem as WorkspaceTreeItem } from './interfaces';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { EOL } from 'os';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
/**
|
||||
* Tree data provider for the workspace main view
|
||||
*/
|
||||
export class WorkspaceTreeDataProvider implements vscode.TreeDataProvider<WorkspaceTreeItem>{
|
||||
constructor(private _workspaceService: IWorkspaceService) { }
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<void | WorkspaceTreeItem | null | undefined> | undefined = new vscode.EventEmitter<WorkspaceTreeItem | undefined | void>();
|
||||
readonly onDidChangeTreeData?: vscode.Event<void | WorkspaceTreeItem | null | undefined> | undefined = this._onDidChangeTreeData?.event;
|
||||
|
||||
refresh(): void {
|
||||
this._onDidChangeTreeData?.fire();
|
||||
}
|
||||
|
||||
getTreeItem(element: WorkspaceTreeItem): vscode.TreeItem | Thenable<vscode.TreeItem> {
|
||||
return element.treeDataProvider.getTreeItem(element.element);
|
||||
}
|
||||
|
||||
async getChildren(element?: WorkspaceTreeItem | undefined): Promise<WorkspaceTreeItem[]> {
|
||||
if (element) {
|
||||
const items = await element.treeDataProvider.getChildren(element.element);
|
||||
return items ? items.map(item => <WorkspaceTreeItem>{ treeDataProvider: element.treeDataProvider, element: item }) : [];
|
||||
}
|
||||
else {
|
||||
// if the element is undefined return the project tree items
|
||||
const projects = await this._workspaceService.getProjectsInWorkspace();
|
||||
const unknownProjects: string[] = [];
|
||||
const treeItems: WorkspaceTreeItem[] = [];
|
||||
let project: string;
|
||||
for (project of projects) {
|
||||
const projectProvider = await this._workspaceService.getProjectProvider(project);
|
||||
if (projectProvider === undefined) {
|
||||
unknownProjects.push(project);
|
||||
continue;
|
||||
}
|
||||
const treeDataProvider = await projectProvider.getProjectTreeDataProvider(project);
|
||||
if (treeDataProvider.onDidChangeTreeData) {
|
||||
treeDataProvider.onDidChangeTreeData((e: any) => {
|
||||
this._onDidChangeTreeData?.fire(e);
|
||||
});
|
||||
}
|
||||
const children = await treeDataProvider.getChildren(element);
|
||||
children?.forEach(child => {
|
||||
treeItems.push({
|
||||
treeDataProvider: treeDataProvider,
|
||||
element: child
|
||||
});
|
||||
});
|
||||
}
|
||||
if (unknownProjects.length > 0) {
|
||||
vscode.window.showErrorMessage(localize('UnknownProjectsError', "No provider was found for the following projects: {0}", unknownProjects.join(EOL)));
|
||||
}
|
||||
return treeItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
extensions/data-workspace/src/dataWorkspaceExtension.ts
Normal file
14
extensions/data-workspace/src/dataWorkspaceExtension.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as dataworkspace from 'dataworkspace';
|
||||
import { ProjectProviderRegistry } from './common/projectProviderRegistry';
|
||||
|
||||
export class DataWorkspaceExtension implements dataworkspace.IExtension {
|
||||
registerProjectProvider(provider: dataworkspace.IProjectProvider): vscode.Disposable {
|
||||
return ProjectProviderRegistry.registerProvider(provider);
|
||||
}
|
||||
}
|
||||
59
extensions/data-workspace/src/dataworkspace.d.ts
vendored
Normal file
59
extensions/data-workspace/src/dataworkspace.d.ts
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'dataworkspace' {
|
||||
import * as vscode from 'vscode';
|
||||
export const enum extension {
|
||||
name = 'Microsoft.data-workspace'
|
||||
}
|
||||
|
||||
/**
|
||||
* dataworkspace extension
|
||||
*/
|
||||
export interface IExtension {
|
||||
/**
|
||||
* register a project provider
|
||||
* @param provider new project provider
|
||||
* @requires a disposable object, upon disposal, the provider will be unregistered.
|
||||
*/
|
||||
registerProjectProvider(provider: IProjectProvider): vscode.Disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the capabilities of project provider
|
||||
*/
|
||||
export interface IProjectProvider {
|
||||
/**
|
||||
* Gets the tree data provider for the given project file
|
||||
* @param projectFilePath The full path of the project file
|
||||
*/
|
||||
getProjectTreeDataProvider(projectFilePath: string): Promise<vscode.TreeDataProvider<any>>;
|
||||
|
||||
/**
|
||||
* Gets the supported project types
|
||||
*/
|
||||
readonly supportedProjectTypes: IProjectType[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the project type
|
||||
*/
|
||||
export interface IProjectType {
|
||||
/**
|
||||
* display name of the project type
|
||||
*/
|
||||
readonly displayName: string;
|
||||
|
||||
/**
|
||||
* project file extension, e.g. sqlproj
|
||||
*/
|
||||
readonly projectFileExtension: string;
|
||||
|
||||
/**
|
||||
* Gets the icon path of the project type
|
||||
*/
|
||||
readonly icon: string | vscode.Uri | { light: string | vscode.Uri, dark: string | vscode.Uri }
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,23 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as dataworkspace from 'dataworkspace';
|
||||
import { WorkspaceTreeDataProvider } from './common/workspaceTreeDataProvider';
|
||||
import { WorkspaceService } from './services/workspaceService';
|
||||
import { DataWorkspaceExtension } from './dataWorkspaceExtension';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
vscode.commands.registerCommand('projects.addProject', () => {
|
||||
});
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<dataworkspace.IExtension> {
|
||||
const workspaceService = new WorkspaceService();
|
||||
const workspaceTreeDataProvider = new WorkspaceTreeDataProvider(workspaceService);
|
||||
context.subscriptions.push(vscode.window.registerTreeDataProvider('dataworkspace.views.main', workspaceTreeDataProvider));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('projects.addProject', () => {
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand('dataworkspace.refresh', () => {
|
||||
workspaceTreeDataProvider.refresh();
|
||||
}));
|
||||
|
||||
return new DataWorkspaceExtension();
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
|
||||
76
extensions/data-workspace/src/services/workspaceService.ts
Normal file
76
extensions/data-workspace/src/services/workspaceService.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as dataworkspace from 'dataworkspace';
|
||||
import * as path from 'path';
|
||||
import { IWorkspaceService } from '../common/interfaces';
|
||||
import { ProjectProviderRegistry } from '../common/projectProviderRegistry';
|
||||
import * as nls from 'vscode-nls';
|
||||
import Logger from '../common/logger';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const WorkspaceConfigurationName = 'dataworkspace';
|
||||
const ProjectsConfigurationName = 'projects';
|
||||
|
||||
export class WorkspaceService implements IWorkspaceService {
|
||||
async getAllProjectTypes(): Promise<dataworkspace.IProjectType[]> {
|
||||
await this.ensureProviderExtensionLoaded();
|
||||
const projectTypes: dataworkspace.IProjectType[] = [];
|
||||
ProjectProviderRegistry.providers.forEach(provider => {
|
||||
projectTypes.push(...provider.supportedProjectTypes);
|
||||
});
|
||||
return projectTypes;
|
||||
}
|
||||
|
||||
async getProjectsInWorkspace(): Promise<string[]> {
|
||||
if (vscode.workspace.workspaceFile) {
|
||||
const projects = <string[]>vscode.workspace.getConfiguration(WorkspaceConfigurationName).get(ProjectsConfigurationName);
|
||||
return projects.map(project => path.isAbsolute(project) ? project : path.join(vscode.workspace.rootPath!, project));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
async getProjectProvider(projectFilePath: string): Promise<dataworkspace.IProjectProvider | undefined> {
|
||||
const projectType = path.extname(projectFilePath).replace(/\./g, '');
|
||||
let provider = ProjectProviderRegistry.getProviderByProjectType(projectType);
|
||||
if (!provider) {
|
||||
await this.ensureProviderExtensionLoaded(projectType);
|
||||
}
|
||||
return ProjectProviderRegistry.getProviderByProjectType(projectType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the project provider extension for the specified project is loaded
|
||||
* @param projectType The file extension of the project, if not specified, all project provider extensions will be loaded.
|
||||
*/
|
||||
private async ensureProviderExtensionLoaded(projectType: string | undefined = undefined): Promise<void> {
|
||||
const inactiveExtensions = vscode.extensions.all.filter(ext => !ext.isActive);
|
||||
const projType = projectType ? projectType.toUpperCase() : undefined;
|
||||
let extension: vscode.Extension<any>;
|
||||
for (extension of inactiveExtensions) {
|
||||
const projectTypes = extension.packageJSON.contributes && extension.packageJSON.contributes.projects as string[];
|
||||
// Process only when this extension is contributing project providers
|
||||
if (projectTypes && projectTypes.length > 0) {
|
||||
if (projType) {
|
||||
if (projectTypes.findIndex((proj: string) => proj.toUpperCase() === projType) !== -1) {
|
||||
await this.activateExtension(extension);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
await this.activateExtension(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async activateExtension(extension: vscode.Extension<any>): Promise<void> {
|
||||
try {
|
||||
await extension.activate();
|
||||
} catch (err) {
|
||||
Logger.error(localize('activateExtensionFailed', "Failed to load the project provider extension '{0}'. Error message: {1}", extension.id, err.message ?? err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as should from 'should';
|
||||
import { DataWorkspaceExtension } from '../dataWorkspaceExtension';
|
||||
import { createProjectProvider } from './projectProviderRegistry.test';
|
||||
import { ProjectProviderRegistry } from '../common/projectProviderRegistry';
|
||||
|
||||
suite('DataWorkspaceExtension Tests', function (): void {
|
||||
test('register and unregister project provider through the extension api', async () => {
|
||||
const extension = new DataWorkspaceExtension();
|
||||
const provider = createProjectProvider([
|
||||
{
|
||||
projectFileExtension: 'testproj',
|
||||
icon: '',
|
||||
displayName: 'test project'
|
||||
}
|
||||
]);
|
||||
const disposable = extension.registerProjectProvider(provider);
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'project provider should have been registered');
|
||||
disposable.dispose();
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be nothing in the ProjectProviderRegistry');
|
||||
});
|
||||
});
|
||||
48
extensions/data-workspace/src/test/index.ts
Normal file
48
extensions/data-workspace/src/test/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
const testRunner = require('vscodetestcover');
|
||||
|
||||
const suite = 'Data Workspace Extension Tests';
|
||||
|
||||
const mochaOptions: any = {
|
||||
ui: 'tdd',
|
||||
useColors: true,
|
||||
timeout: 10000
|
||||
};
|
||||
|
||||
// set relevant mocha options from the environment
|
||||
if (process.env.ADS_TEST_GREP) {
|
||||
mochaOptions.grep = process.env.ADS_TEST_GREP;
|
||||
console.log(`setting options.grep to: ${mochaOptions.grep}`);
|
||||
}
|
||||
if (process.env.ADS_TEST_INVERT_GREP) {
|
||||
mochaOptions.invert = parseInt(process.env.ADS_TEST_INVERT_GREP);
|
||||
console.log(`setting options.invert to: ${mochaOptions.invert}`);
|
||||
}
|
||||
if (process.env.ADS_TEST_TIMEOUT) {
|
||||
mochaOptions.timeout = parseInt(process.env.ADS_TEST_TIMEOUT);
|
||||
console.log(`setting options.timeout to: ${mochaOptions.timeout}`);
|
||||
}
|
||||
if (process.env.ADS_TEST_RETRIES) {
|
||||
mochaOptions.retries = parseInt(process.env.ADS_TEST_RETRIES);
|
||||
console.log(`setting options.retries to: ${mochaOptions.retries}`);
|
||||
}
|
||||
|
||||
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
|
||||
mochaOptions.reporter = 'mocha-multi-reporters';
|
||||
mochaOptions.reporterOptions = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
testsuitesTitle: `${suite} ${process.platform}`,
|
||||
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
testRunner.configure(mochaOptions, { coverConfig: '../../coverConfig.json' });
|
||||
|
||||
export = testRunner;
|
||||
@@ -0,0 +1,100 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as vscode from 'vscode';
|
||||
import * as should from 'should';
|
||||
import { ProjectProviderRegistry } from '../common/projectProviderRegistry';
|
||||
import { IProjectProvider, IProjectType } from 'dataworkspace';
|
||||
|
||||
export class MockTreeDataProvider implements vscode.TreeDataProvider<any>{
|
||||
onDidChangeTreeData?: vscode.Event<any> | undefined;
|
||||
getTreeItem(element: any): vscode.TreeItem | Thenable<vscode.TreeItem> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getChildren(element?: any): vscode.ProviderResult<any[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export function createProjectProvider(projectTypes: IProjectType[]): IProjectProvider {
|
||||
const treeDataProvider = new MockTreeDataProvider();
|
||||
const projectProvider: IProjectProvider = {
|
||||
supportedProjectTypes: projectTypes,
|
||||
getProjectTreeDataProvider: (projectFile: string): Promise<vscode.TreeDataProvider<any>> => {
|
||||
return Promise.resolve(treeDataProvider);
|
||||
}
|
||||
};
|
||||
return projectProvider;
|
||||
}
|
||||
|
||||
suite('ProjectProviderRegistry Tests', function (): void {
|
||||
test('register and unregister project providers', async () => {
|
||||
const provider1 = createProjectProvider([
|
||||
{
|
||||
projectFileExtension: 'testproj',
|
||||
icon: '',
|
||||
displayName: 'test project'
|
||||
}, {
|
||||
projectFileExtension: 'testproj1',
|
||||
icon: '',
|
||||
displayName: 'test project 1'
|
||||
}
|
||||
]);
|
||||
const provider2 = createProjectProvider([
|
||||
{
|
||||
projectFileExtension: 'sqlproj',
|
||||
icon: '',
|
||||
displayName: 'sql project'
|
||||
}
|
||||
]);
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider at the beginning of the test');
|
||||
const disposable1 = ProjectProviderRegistry.registerProvider(provider1);
|
||||
let providerResult = ProjectProviderRegistry.getProviderByProjectType('testproj');
|
||||
should.equal(providerResult, provider1, 'provider1 should be returned for testproj project type');
|
||||
// make sure the project type is case-insensitive for getProviderByProjectType method
|
||||
providerResult = ProjectProviderRegistry.getProviderByProjectType('TeStProJ');
|
||||
should.equal(providerResult, provider1, 'provider1 should be returned for testproj project type');
|
||||
providerResult = ProjectProviderRegistry.getProviderByProjectType('testproj1');
|
||||
should.equal(providerResult, provider1, 'provider1 should be returned for testproj1 project type');
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'there should be only one project provider at this time');
|
||||
const disposable2 = ProjectProviderRegistry.registerProvider(provider2);
|
||||
providerResult = ProjectProviderRegistry.getProviderByProjectType('sqlproj');
|
||||
should.equal(providerResult, provider2, 'provider2 should be returned for sqlproj project type');
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 2, 'there should be 2 project providers at this time');
|
||||
|
||||
// unregister provider1
|
||||
disposable1.dispose();
|
||||
providerResult = ProjectProviderRegistry.getProviderByProjectType('testproj');
|
||||
should.equal(providerResult, undefined, 'undefined should be returned for testproj project type');
|
||||
providerResult = ProjectProviderRegistry.getProviderByProjectType('testproj1');
|
||||
should.equal(providerResult, undefined, 'undefined should be returned for testproj1 project type');
|
||||
providerResult = ProjectProviderRegistry.getProviderByProjectType('sqlproj');
|
||||
should.equal(providerResult, provider2, 'provider2 should be returned for sqlproj project type after provider1 is disposed');
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'there should be only one project provider after unregistering a provider');
|
||||
should.strictEqual(ProjectProviderRegistry.providers[0].supportedProjectTypes[0].projectFileExtension, 'sqlproj', 'the remaining project provider should be sqlproj');
|
||||
|
||||
// unregister provider2
|
||||
disposable2.dispose();
|
||||
providerResult = ProjectProviderRegistry.getProviderByProjectType('sqlproj');
|
||||
should.equal(providerResult, undefined, 'undefined should be returned for sqlproj project type after provider2 is disposed');
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider after unregistering the providers');
|
||||
});
|
||||
|
||||
test('Clear the project provider registry', async () => {
|
||||
const provider = createProjectProvider([
|
||||
{
|
||||
projectFileExtension: 'testproj',
|
||||
icon: '',
|
||||
displayName: 'test project'
|
||||
}
|
||||
]);
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider at the beginning of the test');
|
||||
ProjectProviderRegistry.registerProvider(provider);
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 1, 'there should be only one project provider at this time');
|
||||
ProjectProviderRegistry.clear();
|
||||
should.strictEqual(ProjectProviderRegistry.providers.length, 0, 'there should be no project provider after clearing the registry');
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,8 @@
|
||||
"onCommand:sqlDatabaseProjects.new",
|
||||
"onCommand:sqlDatabaseProjects.open",
|
||||
"onCommand:sqlDatabaseProjects.importDatabase",
|
||||
"workspaceContains:**/*.sqlproj"
|
||||
"workspaceContains:**/*.sqlproj",
|
||||
"onView:dataworkspace.views.main"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"repository": {
|
||||
@@ -25,9 +26,13 @@
|
||||
},
|
||||
"extensionDependencies": [
|
||||
"Microsoft.mssql",
|
||||
"Microsoft.schema-compare"
|
||||
"Microsoft.schema-compare",
|
||||
"Microsoft.data-workspace"
|
||||
],
|
||||
"contributes": {
|
||||
"projects": [
|
||||
"sqlproj"
|
||||
],
|
||||
"configuration": [
|
||||
{
|
||||
"title": "%sqlDatabaseProjects.Settings%",
|
||||
@@ -243,87 +248,87 @@
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "sqlDatabaseProjects.build",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project",
|
||||
"group": "1_dbProjectsFirst@1"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.publish",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project",
|
||||
"group": "1_dbProjectsFirst@2"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.schemaCompare",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project",
|
||||
"group": "1_dbProjectsFirst@3"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newItem",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "2_dbProjects_newMain@1"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newFolder",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "2_dbProjects_newMain@2"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newTable",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "3_dbProjects_newItem@1"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newView",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "3_dbProjects_newItem@2"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newStoredProcedure",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "3_dbProjects_newItem@3"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newScript",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "3_dbProjects_newItem@7"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newPreDeploymentScript",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "3_dbProjects_newItem@8"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.newPostDeploymentScript",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project || viewItem == databaseProject.itemType.folder",
|
||||
"group": "3_dbProjects_newItem@9"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.addDatabaseReference",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.referencesRoot",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.referencesRoot",
|
||||
"group": "4_dbProjects_addDatabaseReference"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.exclude",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.folder || viewItem == databaseProject.itemType.file",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.folder || viewItem == databaseProject.itemType.file",
|
||||
"group": "9_dbProjectsLast@1"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.delete",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.folder || viewItem == databaseProject.itemType.file",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.folder || viewItem == databaseProject.itemType.file",
|
||||
"group": "9_dbProjectsLast@2"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.editProjectFile",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project",
|
||||
"group": "9_dbProjectsLast@7"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.openContainingFolder",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project",
|
||||
"group": "9_dbProjectsLast@8"
|
||||
},
|
||||
{
|
||||
"command": "sqlDatabaseProjects.close",
|
||||
"when": "view == sqlDatabaseProjectsView && viewItem == databaseProject.itemType.project",
|
||||
"when": "view =~ /^(sqlDatabaseProjectsView|dataworkspace.views.main)$/ && viewItem == databaseProject.itemType.project",
|
||||
"group": "9_dbProjectsLast@9"
|
||||
}
|
||||
],
|
||||
@@ -339,7 +344,6 @@
|
||||
"group": "export"
|
||||
}
|
||||
],
|
||||
|
||||
"dataExplorer/context": [
|
||||
{
|
||||
"command": "sqlDatabaseProjects.importDatabase",
|
||||
|
||||
@@ -22,6 +22,9 @@ export const msdbDacpac = 'msdb.dacpac';
|
||||
export const MicrosoftDatatoolsSchemaSqlSql = 'Microsoft.Data.Tools.Schema.Sql.Sql';
|
||||
export const databaseSchemaProvider = 'DatabaseSchemaProvider';
|
||||
|
||||
// Project Provider
|
||||
export const projectTypeDisplayName = localize('projectTypeDisplayName', 'Database Project');
|
||||
|
||||
// commands
|
||||
export const revealFileInOsCommand = 'revealFileInOS';
|
||||
export const schemaCompareStartCommand = 'schemaCompare.start';
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as dataworkspace from 'dataworkspace';
|
||||
import * as templates from '../templates/templates';
|
||||
import * as constants from '../common/constants';
|
||||
import * as path from 'path';
|
||||
@@ -19,6 +20,7 @@ import { NetCoreTool } from '../tools/netcoreTool';
|
||||
import { Project } from '../models/project';
|
||||
import { FileNode, FolderNode } from '../models/tree/fileFolderTreeItem';
|
||||
import { IconPathHelper } from '../common/iconHelper';
|
||||
import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider';
|
||||
|
||||
const SQL_DATABASE_PROJECTS_VIEW_ID = 'sqlDatabaseProjectsView';
|
||||
|
||||
@@ -78,6 +80,7 @@ export default class MainController implements vscode.Disposable {
|
||||
vscode.commands.registerCommand('sqlDatabaseProjects.exclude', async (node: FileNode | FolderNode) => { await this.projectsController.exclude(node); });
|
||||
|
||||
IconPathHelper.setExtensionContext(this.extensionContext);
|
||||
this.registerProjectProvider();
|
||||
|
||||
// init view
|
||||
const treeView = vscode.window.createTreeView(SQL_DATABASE_PROJECTS_VIEW_ID, {
|
||||
@@ -190,6 +193,13 @@ export default class MainController implements vscode.Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private registerProjectProvider(): void {
|
||||
const dataWorkspaceApi: dataworkspace.IExtension = <dataworkspace.IExtension>vscode.extensions.getExtension(dataworkspace.extension.name)?.exports;
|
||||
if (dataWorkspaceApi) {
|
||||
dataWorkspaceApi.registerProjectProvider(new SqlDatabaseProjectProvider());
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.deactivate();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dataworkspace from 'dataworkspace';
|
||||
import * as vscode from 'vscode';
|
||||
import { sqlprojExtension, projectTypeDisplayName } from '../common/constants';
|
||||
import { IconPathHelper } from '../common/iconHelper';
|
||||
import { SqlDatabaseProjectTreeViewProvider } from '../controllers/databaseProjectTreeViewProvider';
|
||||
import { Project } from '../models/project';
|
||||
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
||||
|
||||
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider {
|
||||
|
||||
/**
|
||||
* Gets the project tree data provider
|
||||
* @param projectFilePath The project file path
|
||||
*/
|
||||
async getProjectTreeDataProvider(projectFilePath: string): Promise<vscode.TreeDataProvider<BaseProjectTreeItem>> {
|
||||
const provider = new SqlDatabaseProjectTreeViewProvider();
|
||||
const project = await Project.openProject(projectFilePath);
|
||||
provider.load([project]);
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the supported project types
|
||||
*/
|
||||
get supportedProjectTypes(): dataworkspace.IProjectType[] {
|
||||
return [{
|
||||
projectFileExtension: sqlprojExtension.replace(/\./g, ''),
|
||||
displayName: projectTypeDisplayName,
|
||||
icon: IconPathHelper.databaseProject
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,5 @@
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../src/sql/azdata.d.ts'/>
|
||||
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
||||
/// <reference path='../../../data-workspace/src/dataworkspace.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
|
||||
@@ -22,7 +22,9 @@ const extensionList = [
|
||||
'notebook',
|
||||
'resource-deployment',
|
||||
'machine-learning',
|
||||
'sql-database-projects'];
|
||||
'sql-database-projects',
|
||||
'data-workspace'
|
||||
];
|
||||
|
||||
let argv = require('yargs')
|
||||
.command('$0 [extensions...]')
|
||||
|
||||
Reference in New Issue
Block a user