mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
DacFx import/export wizard (#3162)
Basic wizard to import/export bacpacs and deploy/extract dacpacs
This commit is contained in:
@@ -33,6 +33,15 @@
|
|||||||
"light": "./images/light_icon.svg",
|
"light": "./images/light_icon.svg",
|
||||||
"dark": "./images/dark_icon.svg"
|
"dark": "./images/dark_icon.svg"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "dacFx.start",
|
||||||
|
"title": "Data-tier Application Wizard",
|
||||||
|
"category": "Data-tier Application",
|
||||||
|
"icon": {
|
||||||
|
"light": "./images/light_icon.svg",
|
||||||
|
"dark": "./images/dark_icon.svg"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
@@ -48,6 +57,11 @@
|
|||||||
"command": "flatFileImport.start",
|
"command": "flatFileImport.start",
|
||||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
|
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
|
||||||
"group": "import"
|
"group": "import"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "dacFx.start",
|
||||||
|
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
|
||||||
|
"group": "export"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { FlatFileWizard } from '../wizard/flatFileWizard';
|
|||||||
import { ServiceClient } from '../services/serviceClient';
|
import { ServiceClient } from '../services/serviceClient';
|
||||||
import { ApiType, managerInstance } from '../services/serviceApiManager';
|
import { ApiType, managerInstance } from '../services/serviceApiManager';
|
||||||
import { FlatFileProvider } from '../services/contracts';
|
import { FlatFileProvider } from '../services/contracts';
|
||||||
|
import { DataTierApplicationWizard } from '../wizard/dataTierApplicationWizard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main controller class that initializes the extension
|
* The main controller class that initializes the extension
|
||||||
@@ -35,10 +36,15 @@ export default class MainController extends ControllerBase {
|
|||||||
this.initializeFlatFileProvider(provider);
|
this.initializeFlatFileProvider(provider);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.initializeDacFxWizard();
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeFlatFileProvider(provider: FlatFileProvider) {
|
private initializeFlatFileProvider(provider: FlatFileProvider) {
|
||||||
sqlops.tasks.registerTask('flatFileImport.start', (profile: sqlops.IConnectionProfile, ...args: any[]) => new FlatFileWizard(provider).start(profile, args));
|
sqlops.tasks.registerTask('flatFileImport.start', (profile: sqlops.IConnectionProfile, ...args: any[]) => new FlatFileWizard(provider).start(profile, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initializeDacFxWizard() {
|
||||||
|
sqlops.tasks.registerTask('dacFx.start', (profile: sqlops.IConnectionProfile, ...args: any[]) => new DataTierApplicationWizard().start(profile, args));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
139
extensions/import/src/wizard/api/basePage.ts
Normal file
139
extensions/import/src/wizard/api/basePage.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import { BaseDataModel } from './models';
|
||||||
|
|
||||||
|
export abstract class BasePage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly model: BaseDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method constructs all the elements of the page.
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
public async abstract start(): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the user is entering the page.
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
public async abstract onPageEnter(): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the user is leaving the page.
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async onPageLeave(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this method to cleanup what you don't need cached in the page.
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
public async cleanup(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up a navigation validator.
|
||||||
|
* This will be called right before onPageEnter().
|
||||||
|
*/
|
||||||
|
public abstract setupNavigationValidator();
|
||||||
|
|
||||||
|
protected async getServerValues(): Promise<{ connection, displayName, name }[]> {
|
||||||
|
let cons = await sqlops.connection.getActiveConnections();
|
||||||
|
// This user has no active connections ABORT MISSION
|
||||||
|
if (!cons || cons.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = -1;
|
||||||
|
let idx = -1;
|
||||||
|
|
||||||
|
|
||||||
|
let values = cons.map(c => {
|
||||||
|
// Handle the code to remember what the user's choice was from before
|
||||||
|
count++;
|
||||||
|
if (idx === -1) {
|
||||||
|
if (this.model.server && c.connectionId === this.model.server.connectionId) {
|
||||||
|
idx = count;
|
||||||
|
} else if (this.model.serverId && c.connectionId === this.model.serverId) {
|
||||||
|
idx = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = c.options.databaseDisplayName;
|
||||||
|
let usr = c.options.user;
|
||||||
|
let srv = c.options.server;
|
||||||
|
|
||||||
|
if (!db) {
|
||||||
|
db = '<default>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usr) {
|
||||||
|
usr = 'default';
|
||||||
|
}
|
||||||
|
|
||||||
|
let finalName = `${srv}, ${db} (${usr})`;
|
||||||
|
return {
|
||||||
|
connection: c,
|
||||||
|
displayName: finalName,
|
||||||
|
name: c.connectionId
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (idx >= 0) {
|
||||||
|
let tmp = values[0];
|
||||||
|
values[0] = values[idx];
|
||||||
|
values[idx] = tmp;
|
||||||
|
} else {
|
||||||
|
this.deleteServerValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async getDatabaseValues(): Promise<{ displayName, name }[]> {
|
||||||
|
let idx = -1;
|
||||||
|
let count = -1;
|
||||||
|
let values = (await sqlops.connection.listDatabases(this.model.server.connectionId)).map(db => {
|
||||||
|
count++;
|
||||||
|
if (this.model.database && db === this.model.database) {
|
||||||
|
idx = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
displayName: db,
|
||||||
|
name: db
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (idx >= 0) {
|
||||||
|
let tmp = values[0];
|
||||||
|
values[0] = values[idx];
|
||||||
|
values[idx] = tmp;
|
||||||
|
} else {
|
||||||
|
this.deleteDatabaseValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected deleteServerValues() {
|
||||||
|
delete this.model.server;
|
||||||
|
delete this.model.serverId;
|
||||||
|
delete this.model.database;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected deleteDatabaseValues() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
158
extensions/import/src/wizard/api/dacFxConfigPage.ts
Normal file
158
extensions/import/src/wizard/api/dacFxConfigPage.ts
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||||
|
import { DacFxDataModel } from './models';
|
||||||
|
import { BasePage } from './basePage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export abstract class DacFxConfigPage extends BasePage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
protected serverDropdown: sqlops.DropDownComponent;
|
||||||
|
protected databaseTextBox: sqlops.InputBoxComponent;
|
||||||
|
protected databaseDropdown: sqlops.DropDownComponent;
|
||||||
|
protected databaseLoader: sqlops.LoadingComponent;
|
||||||
|
protected fileTextBox: sqlops.InputBoxComponent;
|
||||||
|
protected fileButton: sqlops.ButtonComponent;
|
||||||
|
protected fileExtension: string;
|
||||||
|
|
||||||
|
protected constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) {
|
||||||
|
super();
|
||||||
|
this.instance = instance;
|
||||||
|
this.wizardPage = wizardPage;
|
||||||
|
this.model = model;
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setupNavigationValidator() {
|
||||||
|
this.instance.registerNavigationValidator(() => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createServerDropdown(isTargetServer: boolean): Promise<sqlops.FormComponent> {
|
||||||
|
this.serverDropdown = this.view.modelBuilder.dropDown().withProperties({
|
||||||
|
required: true
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
// Handle server changes
|
||||||
|
this.serverDropdown.onValueChanged(async () => {
|
||||||
|
this.model.server = (this.serverDropdown.value as ConnectionDropdownValue).connection;
|
||||||
|
this.model.serverName = (this.serverDropdown.value as ConnectionDropdownValue).displayName;
|
||||||
|
await this.populateDatabaseDropdown();
|
||||||
|
});
|
||||||
|
|
||||||
|
let targetServerTitle = localize('dacFx.targetServerDropdownTitle', 'Target Server');
|
||||||
|
let sourceServerTitle = localize('dacFx.sourceServerDropdownTitle', 'Source Server');
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.serverDropdown,
|
||||||
|
title: isTargetServer ? targetServerTitle : sourceServerTitle
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async populateServerDropdown(): Promise<boolean> {
|
||||||
|
let values = await this.getServerValues();
|
||||||
|
if (values === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model.server = values[0].connection;
|
||||||
|
this.model.serverName = values[0].displayName;
|
||||||
|
|
||||||
|
this.serverDropdown.updateProperties({
|
||||||
|
values: values
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createDatabaseTextBox(): Promise<sqlops.FormComponent> {
|
||||||
|
this.databaseTextBox = this.view.modelBuilder.inputBox().withProperties({
|
||||||
|
required: true
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.databaseTextBox.onTextChanged(async () => {
|
||||||
|
this.model.database = this.databaseTextBox.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.databaseTextBox,
|
||||||
|
title: localize('dacFx.databaseNameTextBox', 'Target Database')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createDatabaseDropdown(): Promise<sqlops.FormComponent> {
|
||||||
|
this.databaseDropdown = this.view.modelBuilder.dropDown().withProperties({
|
||||||
|
required: true
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
// Handle database changes
|
||||||
|
this.databaseDropdown.onValueChanged(async () => {
|
||||||
|
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
||||||
|
this.fileTextBox.value = this.generateFilePath();
|
||||||
|
this.model.filePath = this.fileTextBox.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.databaseLoader = this.view.modelBuilder.loadingComponent().withItem(this.databaseDropdown).component();
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.databaseLoader,
|
||||||
|
title: localize('dacFx.sourceDatabaseDropdownTitle', 'Source Database')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async populateDatabaseDropdown(): Promise<boolean> {
|
||||||
|
this.databaseLoader.loading = true;
|
||||||
|
this.databaseDropdown.updateProperties({ values: [] });
|
||||||
|
|
||||||
|
if (!this.model.server) {
|
||||||
|
this.databaseLoader.loading = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let values = await this.getDatabaseValues();
|
||||||
|
this.model.database = values[0].name;
|
||||||
|
this.model.filePath = this.generateFilePath();
|
||||||
|
this.fileTextBox.value = this.model.filePath;
|
||||||
|
|
||||||
|
this.databaseDropdown.updateProperties({
|
||||||
|
values: values
|
||||||
|
});
|
||||||
|
this.databaseLoader.loading = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createFileBrowserParts() {
|
||||||
|
this.fileTextBox = this.view.modelBuilder.inputBox().withProperties({
|
||||||
|
required: true
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.fileButton = this.view.modelBuilder.button().withProperties({
|
||||||
|
label: '•••',
|
||||||
|
}).component();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected generateFilePath(): string {
|
||||||
|
let now = new Date();
|
||||||
|
let datetime = now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate() + '-' + now.getHours() + '-' + now.getMinutes();
|
||||||
|
return path.join(os.homedir(), this.model.database + '-' + datetime + this.fileExtension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConnectionDropdownValue extends sqlops.CategoryValue {
|
||||||
|
connection: sqlops.connection.Connection;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8,8 +8,9 @@ import { ImportDataModel } from './models';
|
|||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { FlatFileProvider } from '../../services/contracts';
|
import { FlatFileProvider } from '../../services/contracts';
|
||||||
import { FlatFileWizard } from '../flatFileWizard';
|
import { FlatFileWizard } from '../flatFileWizard';
|
||||||
|
import { BasePage } from './basePage';
|
||||||
|
|
||||||
export abstract class ImportPage {
|
export abstract class ImportPage extends BasePage {
|
||||||
|
|
||||||
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
protected readonly instance: FlatFileWizard;
|
protected readonly instance: FlatFileWizard;
|
||||||
@@ -18,42 +19,11 @@ export abstract class ImportPage {
|
|||||||
protected readonly provider: FlatFileProvider;
|
protected readonly provider: FlatFileProvider;
|
||||||
|
|
||||||
protected constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
|
protected constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
|
||||||
|
super();
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.wizardPage = wizardPage;
|
this.wizardPage = wizardPage;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.view = view;
|
this.view = view;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method constructs all the elements of the page.
|
|
||||||
* @returns {Promise<boolean>}
|
|
||||||
*/
|
|
||||||
public async abstract start(): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when the user is entering the page.
|
|
||||||
* @returns {Promise<boolean>}
|
|
||||||
*/
|
|
||||||
public async abstract onPageEnter(): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when the user is leaving the page.
|
|
||||||
* @returns {Promise<boolean>}
|
|
||||||
*/
|
|
||||||
public async abstract onPageLeave(): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up a navigation validator.
|
|
||||||
* This will be called right before onPageEnter().
|
|
||||||
*/
|
|
||||||
public abstract setupNavigationValidator();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override this method to cleanup what you don't need cached in the page.
|
|
||||||
* @returns {Promise<boolean>}
|
|
||||||
*/
|
|
||||||
public async cleanup(): Promise<boolean> {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,19 @@
|
|||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
|
export interface BaseDataModel {
|
||||||
|
server: sqlops.connection.Connection;
|
||||||
|
serverId: string;
|
||||||
|
database: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main data model that communicates between the pages.
|
* The main data model that communicates between the pages.
|
||||||
*/
|
*/
|
||||||
export interface ImportDataModel {
|
export interface ImportDataModel extends BaseDataModel {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
proseColumns: ColumnMetadata[];
|
proseColumns: ColumnMetadata[];
|
||||||
proseDataPreview: string[][];
|
proseDataPreview: string[][];
|
||||||
server: sqlops.connection.Connection;
|
|
||||||
serverId: string;
|
|
||||||
database: string;
|
database: string;
|
||||||
table: string;
|
table: string;
|
||||||
schema: string;
|
schema: string;
|
||||||
@@ -31,3 +35,14 @@ export interface ColumnMetadata {
|
|||||||
primaryKey: boolean;
|
primaryKey: boolean;
|
||||||
nullable: boolean;
|
nullable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data model to communicate between DacFx pages
|
||||||
|
*/
|
||||||
|
export interface DacFxDataModel extends BaseDataModel {
|
||||||
|
serverName: string;
|
||||||
|
serverId: string;
|
||||||
|
filePath: string;
|
||||||
|
version: string;
|
||||||
|
upgradeExisting: boolean;
|
||||||
|
}
|
||||||
|
|||||||
253
extensions/import/src/wizard/dataTierApplicationWizard.ts
Normal file
253
extensions/import/src/wizard/dataTierApplicationWizard.ts
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 vscode from 'vscode';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import { SelectOperationPage } from './pages/selectOperationpage';
|
||||||
|
import { DeployConfigPage } from './pages/deployConfigPage';
|
||||||
|
import { DacFxSummaryPage } from './pages/dacFxSummaryPage';
|
||||||
|
import { ExportConfigPage } from './pages/exportConfigPage';
|
||||||
|
import { ExtractConfigPage } from './pages/extractConfigPage';
|
||||||
|
import { ImportConfigPage } from './pages/importConfigPage';
|
||||||
|
import { DacFxDataModel } from './api/models';
|
||||||
|
import { BasePage } from './api/basePage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
class Page {
|
||||||
|
wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
dacFxPage: BasePage;
|
||||||
|
|
||||||
|
constructor(wizardPage: sqlops.window.modelviewdialog.WizardPage) {
|
||||||
|
this.wizardPage = wizardPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Operation {
|
||||||
|
deploy,
|
||||||
|
extract,
|
||||||
|
import,
|
||||||
|
export
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DataTierApplicationWizard {
|
||||||
|
public wizard: sqlops.window.modelviewdialog.Wizard;
|
||||||
|
private connection: sqlops.connection.Connection;
|
||||||
|
private model: DacFxDataModel;
|
||||||
|
public pages: Map<string, Page> = new Map<string, Page>();
|
||||||
|
public selectedOperation: Operation;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start(p: any, ...args: any[]) {
|
||||||
|
this.model = <DacFxDataModel>{};
|
||||||
|
|
||||||
|
let profile = p ? <sqlops.IConnectionProfile>p.connectionProfile : undefined;
|
||||||
|
if (profile) {
|
||||||
|
this.model.serverId = profile.id;
|
||||||
|
this.model.database = profile.databaseName;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connection = await sqlops.connection.getCurrentConnection();
|
||||||
|
if (!this.connection) {
|
||||||
|
this.connection = await sqlops.connection.openConnectionDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wizard = sqlops.window.modelviewdialog.createWizard('Data-tier Application Wizard');
|
||||||
|
let selectOperationWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.selectOperationPageName', 'Select an Operation'));
|
||||||
|
let deployConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.deployConfigPageName', 'Select Deploy Dacpac Settings'));
|
||||||
|
let summaryWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.summaryPageName', 'Summary'));
|
||||||
|
let extractConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.extractConfigPageName', 'Select Extract Dacpac Settings'));
|
||||||
|
let importConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.importConfigPageName', 'Select Import Bacpac Settings'));
|
||||||
|
let exportConfigWizardPage = sqlops.window.modelviewdialog.createWizardPage(localize('dacFx.exportConfigPageName', 'Select Export Bacpac Settings'));
|
||||||
|
|
||||||
|
this.pages.set('selectOperation', new Page(selectOperationWizardPage));
|
||||||
|
this.pages.set('deployConfig', new Page(deployConfigWizardPage));
|
||||||
|
this.pages.set('extractConfig', new Page(extractConfigWizardPage));
|
||||||
|
this.pages.set('importConfig', new Page(importConfigWizardPage));
|
||||||
|
this.pages.set('exportConfig', new Page(exportConfigWizardPage));
|
||||||
|
this.pages.set('summary', new Page(summaryWizardPage));
|
||||||
|
|
||||||
|
selectOperationWizardPage.registerContent(async (view) => {
|
||||||
|
let selectOperationDacFxPage = new SelectOperationPage(this, selectOperationWizardPage, this.model, view);
|
||||||
|
this.pages.get('selectOperation').dacFxPage = selectOperationDacFxPage;
|
||||||
|
await selectOperationDacFxPage.start().then(() => {
|
||||||
|
selectOperationDacFxPage.setupNavigationValidator();
|
||||||
|
selectOperationDacFxPage.onPageEnter();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
deployConfigWizardPage.registerContent(async (view) => {
|
||||||
|
let deployConfigDacFxPage = new DeployConfigPage(this, deployConfigWizardPage, this.model, view);
|
||||||
|
this.pages.get('deployConfig').dacFxPage = deployConfigDacFxPage;
|
||||||
|
await deployConfigDacFxPage.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
extractConfigWizardPage.registerContent(async (view) => {
|
||||||
|
let extractConfigDacFxPage = new ExtractConfigPage(this, extractConfigWizardPage, this.model, view);
|
||||||
|
this.pages.get('extractConfig').dacFxPage = extractConfigDacFxPage;
|
||||||
|
await extractConfigDacFxPage.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
importConfigWizardPage.registerContent(async (view) => {
|
||||||
|
let importConfigDacFxPage = new ImportConfigPage(this, importConfigWizardPage, this.model, view);
|
||||||
|
this.pages.get('importConfig').dacFxPage = importConfigDacFxPage;
|
||||||
|
await importConfigDacFxPage.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
exportConfigWizardPage.registerContent(async (view) => {
|
||||||
|
let exportConfigDacFxPage = new ExportConfigPage(this, exportConfigWizardPage, this.model, view);
|
||||||
|
this.pages.get('exportConfig').dacFxPage = exportConfigDacFxPage;
|
||||||
|
await exportConfigDacFxPage.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
summaryWizardPage.registerContent(async (view) => {
|
||||||
|
let summaryDacFxPage = new DacFxSummaryPage(this, summaryWizardPage, this.model, view);
|
||||||
|
this.pages.get('summary').dacFxPage = summaryDacFxPage;
|
||||||
|
await summaryDacFxPage.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.wizard.onPageChanged(async (event) => {
|
||||||
|
let idx = event.newPage;
|
||||||
|
let page: Page;
|
||||||
|
|
||||||
|
if (idx === 1) {
|
||||||
|
switch (this.selectedOperation) {
|
||||||
|
case Operation.deploy: {
|
||||||
|
page = this.pages.get('deployConfig');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.extract: {
|
||||||
|
page = this.pages.get('extractConfig');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.import: {
|
||||||
|
page = this.pages.get('importConfig');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.export: {
|
||||||
|
page = this.pages.get('exportConfig');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (idx === 2) {
|
||||||
|
page = this.pages.get('summary');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page !== undefined) {
|
||||||
|
page.dacFxPage.setupNavigationValidator();
|
||||||
|
page.dacFxPage.onPageEnter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.wizard.pages = [selectOperationWizardPage, deployConfigWizardPage, summaryWizardPage];
|
||||||
|
this.wizard.generateScriptButton.hidden = true;
|
||||||
|
this.wizard.doneButton.onClick(async () => await this.executeOperation());
|
||||||
|
|
||||||
|
this.wizard.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean) {
|
||||||
|
this.wizard.registerNavigationValidator(validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setDoneButton(operation: Operation): void {
|
||||||
|
switch (operation) {
|
||||||
|
case Operation.deploy: {
|
||||||
|
this.wizard.doneButton.label = localize('dacFx.deployButton', 'Deploy');
|
||||||
|
this.selectedOperation = Operation.deploy;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.extract: {
|
||||||
|
this.wizard.doneButton.label = localize('dacFx.extractButton', 'Extract');
|
||||||
|
this.selectedOperation = Operation.extract;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.import: {
|
||||||
|
this.wizard.doneButton.label = localize('dacFx.importButton', 'Import');
|
||||||
|
this.selectedOperation = Operation.import;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.export: {
|
||||||
|
this.wizard.doneButton.label = localize('dacFx.exportButton', 'Export');
|
||||||
|
this.selectedOperation = Operation.export;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async executeOperation() {
|
||||||
|
switch (this.selectedOperation) {
|
||||||
|
case Operation.deploy: {
|
||||||
|
await this.deploy();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.extract: {
|
||||||
|
await this.extract();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.import: {
|
||||||
|
await this.import();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.export: {
|
||||||
|
await this.export();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async deploy() {
|
||||||
|
let service = await DataTierApplicationWizard.getService();
|
||||||
|
let ownerUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||||
|
|
||||||
|
let result = await service.deployDacpac(this.model.filePath, this.model.database, this.model.upgradeExisting, ownerUri, sqlops.TaskExecutionMode.execute);
|
||||||
|
if (!result || !result.success) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
localize('alertData.deployErrorMessage', "Deploy failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async extract() {
|
||||||
|
let service = await DataTierApplicationWizard.getService();
|
||||||
|
let ownerUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||||
|
|
||||||
|
let result = await service.extractDacpac(this.model.database, this.model.filePath, this.model.database, this.model.version, ownerUri, sqlops.TaskExecutionMode.execute);
|
||||||
|
if (!result || !result.success) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
localize('alertData.extractErrorMessage', "Extract failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async export() {
|
||||||
|
let service = await DataTierApplicationWizard.getService();
|
||||||
|
let ownerUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||||
|
|
||||||
|
let result = await service.exportBacpac(this.model.database, this.model.filePath, ownerUri, sqlops.TaskExecutionMode.execute);
|
||||||
|
if (!result || !result.success) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
localize('alertData.exportErrorMessage', "Export failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async import() {
|
||||||
|
let service = await DataTierApplicationWizard.getService();
|
||||||
|
let ownerUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||||
|
|
||||||
|
let result = await service.importBacpac(this.model.filePath, this.model.database, ownerUri, sqlops.TaskExecutionMode.execute);
|
||||||
|
if (!result || !result.success) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
localize('alertData.importErrorMessage', "Import failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getService(): Promise<sqlops.DacFxServicesProvider> {
|
||||||
|
let currentConnection = await sqlops.connection.getCurrentConnection();
|
||||||
|
let service = sqlops.dataprotocol.getProvider<sqlops.DacFxServicesProvider>(currentConnection.providerName, sqlops.DataProviderType.DacFxServicesProvider);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
}
|
||||||
112
extensions/import/src/wizard/pages/dacFxSummaryPage.ts
Normal file
112
extensions/import/src/wizard/pages/dacFxSummaryPage.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import { DacFxDataModel } from '../api/models';
|
||||||
|
import { DataTierApplicationWizard, Operation } from '../dataTierApplicationWizard';
|
||||||
|
import { BasePage } from '../api/basePage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class DacFxSummaryPage extends BasePage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
|
||||||
|
private form: sqlops.FormContainer;
|
||||||
|
private table: sqlops.TableComponent;
|
||||||
|
private loader: sqlops.LoadingComponent;
|
||||||
|
|
||||||
|
public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) {
|
||||||
|
super();
|
||||||
|
this.instance = instance;
|
||||||
|
this.wizardPage = wizardPage;
|
||||||
|
this.model = model;
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
this.table = this.view.modelBuilder.table().component();
|
||||||
|
this.loader = this.view.modelBuilder.loadingComponent().withItem(this.table).component();
|
||||||
|
this.form = this.view.modelBuilder.formContainer().withFormItems(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
component: this.table,
|
||||||
|
title: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
).component();
|
||||||
|
await this.view.initializeModel(this.form);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPageEnter(): Promise<boolean> {
|
||||||
|
this.populateTable();
|
||||||
|
this.loader.loading = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setupNavigationValidator() {
|
||||||
|
this.instance.registerNavigationValidator(() => {
|
||||||
|
if (this.loader.loading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private populateTable() {
|
||||||
|
let data = [];
|
||||||
|
let targetServer = localize('dacfx.targetServerName', 'Target Server');
|
||||||
|
let targetDatabase = localize('dacfx.targetDatabaseName', 'Target Database');
|
||||||
|
let sourceServer = localize('dacfx.sourceServerName', 'Source Server');
|
||||||
|
let sourceDatabase = localize('dacfx.sourceDatabaseName', 'Source Database');
|
||||||
|
let fileLocation = localize('dacfx.fileLocation', 'File Location');
|
||||||
|
|
||||||
|
switch (this.instance.selectedOperation) {
|
||||||
|
case Operation.deploy: {
|
||||||
|
data = [
|
||||||
|
[targetServer, this.model.serverName],
|
||||||
|
[fileLocation, this.model.filePath],
|
||||||
|
[targetDatabase, this.model.database]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.extract: {
|
||||||
|
data = [
|
||||||
|
[sourceServer, this.model.serverName],
|
||||||
|
[sourceDatabase, this.model.database],
|
||||||
|
[localize('dacfxExtract.version', 'Version'), this.model.version],
|
||||||
|
[fileLocation, this.model.filePath]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.import: {
|
||||||
|
data = [
|
||||||
|
[targetServer, this.model.serverName],
|
||||||
|
[fileLocation, this.model.filePath],
|
||||||
|
[targetDatabase, this.model.database]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Operation.export: {
|
||||||
|
data = [
|
||||||
|
[sourceServer, this.model.serverName],
|
||||||
|
[sourceDatabase, this.model.database],
|
||||||
|
[fileLocation, this.model.filePath]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.table.updateProperties({
|
||||||
|
data: data,
|
||||||
|
columns: ['Setting', 'Value'],
|
||||||
|
width: 600,
|
||||||
|
height: 200
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
190
extensions/import/src/wizard/pages/deployConfigPage.ts
Normal file
190
extensions/import/src/wizard/pages/deployConfigPage.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { DacFxDataModel } from '../api/models';
|
||||||
|
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||||
|
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class DeployConfigPage extends DacFxConfigPage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
private databaseDropdownComponent: sqlops.FormComponent;
|
||||||
|
private databaseComponent: sqlops.FormComponent;
|
||||||
|
private formBuilder: sqlops.FormBuilder;
|
||||||
|
private form: sqlops.FormContainer;
|
||||||
|
|
||||||
|
public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) {
|
||||||
|
super(instance, wizardPage, model, view);
|
||||||
|
this.fileExtension = '.bacpac';
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
let serverComponent = await this.createServerDropdown(true);
|
||||||
|
let fileBrowserComponent = await this.createFileBrowser();
|
||||||
|
this.databaseComponent = await this.createDatabaseTextBox();
|
||||||
|
this.databaseComponent.title = localize('dacFx.databaseNameTextBox', 'Database Name');
|
||||||
|
this.databaseDropdownComponent = await this.createDeployDatabaseDropdown();
|
||||||
|
this.databaseDropdownComponent.title = localize('dacFx.databaseNameDropdown', 'Database Name');
|
||||||
|
let radioButtons = await this.createRadiobuttons();
|
||||||
|
|
||||||
|
this.formBuilder = this.view.modelBuilder.formContainer()
|
||||||
|
.withFormItems(
|
||||||
|
[
|
||||||
|
fileBrowserComponent,
|
||||||
|
serverComponent,
|
||||||
|
radioButtons,
|
||||||
|
this.databaseDropdownComponent
|
||||||
|
], {
|
||||||
|
horizontal: true,
|
||||||
|
componentWidth: 400
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form = this.formBuilder.component();
|
||||||
|
await this.view.initializeModel(this.form);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPageEnter(): Promise<boolean> {
|
||||||
|
let r1 = await this.populateServerDropdown();
|
||||||
|
let r2 = await this.populateDeployDatabaseDropdown();
|
||||||
|
return r1 && r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createFileBrowser(): Promise<sqlops.FormComponent> {
|
||||||
|
this.createFileBrowserParts();
|
||||||
|
|
||||||
|
this.fileButton.onDidClick(async (click) => {
|
||||||
|
let fileUris = await vscode.window.showOpenDialog(
|
||||||
|
{
|
||||||
|
canSelectFiles: true,
|
||||||
|
canSelectFolders: false,
|
||||||
|
canSelectMany: false,
|
||||||
|
defaultUri: vscode.Uri.file(os.homedir()),
|
||||||
|
openLabel: localize('dacFxDeploy.openFile', 'Open'),
|
||||||
|
filters: {
|
||||||
|
'dacpac Files': ['dacpac'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fileUris || fileUris.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileUri = fileUris[0];
|
||||||
|
this.fileTextBox.value = fileUri.fsPath;
|
||||||
|
this.model.filePath = fileUri.fsPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fileTextBox.onTextChanged(async () => {
|
||||||
|
this.model.filePath = this.fileTextBox.value;
|
||||||
|
this.databaseTextBox.value = this.generateDatabaseName(this.model.filePath);
|
||||||
|
if (!this.model.upgradeExisting) {
|
||||||
|
this.model.database = this.databaseTextBox.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.fileTextBox,
|
||||||
|
title: localize('dacFxDeploy.fileTextboxTitle', 'File Location'),
|
||||||
|
actions: [this.fileButton]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createRadiobuttons(): Promise<sqlops.FormComponent> {
|
||||||
|
let upgradeRadioButton = this.view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
name: 'updateExisting',
|
||||||
|
label: localize('dacFx.upgradeRadioButtonLabel', 'Upgrade Existing Database'),
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
let newRadioButton = this.view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
name: 'updateExisting',
|
||||||
|
label: localize('dacFx.newRadioButtonLabel', 'New Database'),
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
upgradeRadioButton.onDidClick(() => {
|
||||||
|
this.model.upgradeExisting = true;
|
||||||
|
this.formBuilder.removeFormItem(this.databaseComponent);
|
||||||
|
this.formBuilder.addFormItem(this.databaseDropdownComponent, { horizontal: true, componentWidth: 400 });
|
||||||
|
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
||||||
|
});
|
||||||
|
|
||||||
|
newRadioButton.onDidClick(() => {
|
||||||
|
this.model.upgradeExisting = false;
|
||||||
|
this.formBuilder.removeFormItem(this.databaseDropdownComponent);
|
||||||
|
this.formBuilder.addFormItem(this.databaseComponent, { horizontal: true, componentWidth: 400 });
|
||||||
|
this.model.database = this.databaseTextBox.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize with upgrade existing true
|
||||||
|
upgradeRadioButton.checked = true;
|
||||||
|
this.model.upgradeExisting = true;
|
||||||
|
|
||||||
|
let flexRadioButtonsModel = this.view.modelBuilder.flexContainer()
|
||||||
|
.withLayout({
|
||||||
|
flexFlow: 'row',
|
||||||
|
}).withItems([
|
||||||
|
upgradeRadioButton, newRadioButton]
|
||||||
|
).component();
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: flexRadioButtonsModel,
|
||||||
|
title: localize('dacFx.targetDatabaseRadioButtonsTitle', 'Target Database')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createDeployDatabaseDropdown(): Promise<sqlops.FormComponent> {
|
||||||
|
this.databaseDropdown = this.view.modelBuilder.dropDown().withProperties({
|
||||||
|
required: true
|
||||||
|
}).component();
|
||||||
|
// Handle database changes
|
||||||
|
this.databaseDropdown.onValueChanged(async () => {
|
||||||
|
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
||||||
|
});
|
||||||
|
this.databaseLoader = this.view.modelBuilder.loadingComponent().withItem(this.databaseDropdown).component();
|
||||||
|
return {
|
||||||
|
component: this.databaseLoader,
|
||||||
|
title: localize('dacFx.targetDatabaseDropdownTitle', 'Database Name')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async populateDeployDatabaseDropdown(): Promise<boolean> {
|
||||||
|
this.databaseLoader.loading = true;
|
||||||
|
this.databaseDropdown.updateProperties({ values: [] });
|
||||||
|
if (!this.model.server) {
|
||||||
|
this.databaseLoader.loading = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let values = await this.getDatabaseValues();
|
||||||
|
|
||||||
|
if (this.model.database === undefined) {
|
||||||
|
this.model.database = values[0].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.databaseDropdown.updateProperties({
|
||||||
|
values: values
|
||||||
|
});
|
||||||
|
this.databaseLoader.loading = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateDatabaseName(filePath: string): string {
|
||||||
|
let result = path.parse(filePath);
|
||||||
|
return result.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
100
extensions/import/src/wizard/pages/exportConfigPage.ts
Normal file
100
extensions/import/src/wizard/pages/exportConfigPage.ts
Normal file
@@ -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.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { DacFxDataModel } from '../api/models';
|
||||||
|
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||||
|
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class ExportConfigPage extends DacFxConfigPage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
|
||||||
|
private form: sqlops.FormContainer;
|
||||||
|
|
||||||
|
public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) {
|
||||||
|
super(instance, wizardPage, model, view);
|
||||||
|
this.fileExtension = '.bacpac';
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
let databaseComponent = await this.createDatabaseDropdown();
|
||||||
|
let serverComponent = await this.createServerDropdown(false);
|
||||||
|
let fileBrowserComponent = await this.createFileBrowser();
|
||||||
|
|
||||||
|
this.form = this.view.modelBuilder.formContainer()
|
||||||
|
.withFormItems(
|
||||||
|
[
|
||||||
|
serverComponent,
|
||||||
|
databaseComponent,
|
||||||
|
fileBrowserComponent,
|
||||||
|
], {
|
||||||
|
horizontal: true,
|
||||||
|
componentWidth: 400
|
||||||
|
}).component();
|
||||||
|
await this.view.initializeModel(this.form);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPageEnter(): Promise<boolean> {
|
||||||
|
let r1 = await this.populateServerDropdown();
|
||||||
|
let r2 = await this.populateDatabaseDropdown();
|
||||||
|
return r1 && r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setupNavigationValidator() {
|
||||||
|
this.instance.registerNavigationValidator(() => {
|
||||||
|
if (this.databaseLoader.loading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createFileBrowser(): Promise<sqlops.FormComponent> {
|
||||||
|
this.createFileBrowserParts();
|
||||||
|
|
||||||
|
// default filepath
|
||||||
|
this.fileTextBox.value = this.generateFilePath();
|
||||||
|
this.model.filePath = this.fileTextBox.value;
|
||||||
|
|
||||||
|
this.fileButton.onDidClick(async (click) => {
|
||||||
|
let fileUri = await vscode.window.showSaveDialog(
|
||||||
|
{
|
||||||
|
defaultUri: vscode.Uri.file(this.fileTextBox.value),
|
||||||
|
saveLabel: localize('dacfxExport.saveFile', 'Save'),
|
||||||
|
filters: {
|
||||||
|
'bacpac Files': ['bacpac'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fileUri) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileTextBox.value = fileUri.fsPath;
|
||||||
|
this.model.filePath = fileUri.fsPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fileTextBox.onTextChanged(async () => {
|
||||||
|
this.model.filePath = this.fileTextBox.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.fileTextBox,
|
||||||
|
title: localize('dacFxExport.fileTextboxTitle', 'File Location'),
|
||||||
|
actions: [this.fileButton]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
122
extensions/import/src/wizard/pages/extractConfigPage.ts
Normal file
122
extensions/import/src/wizard/pages/extractConfigPage.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { DacFxDataModel } from '../api/models';
|
||||||
|
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||||
|
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class ExtractConfigPage extends DacFxConfigPage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
|
||||||
|
private form: sqlops.FormContainer;
|
||||||
|
private versionTextBox: sqlops.InputBoxComponent;
|
||||||
|
|
||||||
|
public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) {
|
||||||
|
super(instance, wizardPage, model, view);
|
||||||
|
this.fileExtension = '.dacpac';
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
let databaseComponent = await this.createDatabaseDropdown();
|
||||||
|
let serverComponent = await this.createServerDropdown(false);
|
||||||
|
let fileBrowserComponent = await this.createFileBrowser();
|
||||||
|
let versionComponent = await this.createVersionTextBox();
|
||||||
|
|
||||||
|
this.form = this.view.modelBuilder.formContainer()
|
||||||
|
.withFormItems(
|
||||||
|
[
|
||||||
|
serverComponent,
|
||||||
|
databaseComponent,
|
||||||
|
versionComponent,
|
||||||
|
fileBrowserComponent,
|
||||||
|
], {
|
||||||
|
horizontal: true,
|
||||||
|
componentWidth: 400
|
||||||
|
}).component();
|
||||||
|
await this.view.initializeModel(this.form);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPageEnter(): Promise<boolean> {
|
||||||
|
let r1 = await this.populateServerDropdown();
|
||||||
|
let r2 = await this.populateDatabaseDropdown();
|
||||||
|
return r1 && r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setupNavigationValidator() {
|
||||||
|
this.instance.registerNavigationValidator(() => {
|
||||||
|
if (this.databaseLoader.loading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createFileBrowser(): Promise<sqlops.FormComponent> {
|
||||||
|
this.createFileBrowserParts();
|
||||||
|
|
||||||
|
// default filepath
|
||||||
|
this.fileTextBox.value = this.generateFilePath();
|
||||||
|
this.model.filePath = this.fileTextBox.value;
|
||||||
|
|
||||||
|
this.fileButton.onDidClick(async (click) => {
|
||||||
|
let fileUri = await vscode.window.showSaveDialog(
|
||||||
|
{
|
||||||
|
defaultUri: vscode.Uri.file(this.fileTextBox.value),
|
||||||
|
saveLabel: localize('dacfxExtract.saveFile', 'Save'),
|
||||||
|
filters: {
|
||||||
|
'dacpac Files': ['dacpac'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fileUri) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileTextBox.value = fileUri.fsPath;
|
||||||
|
this.model.filePath = fileUri.fsPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fileTextBox.onTextChanged(async () => {
|
||||||
|
this.model.filePath = this.fileTextBox.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.fileTextBox,
|
||||||
|
title: localize('dacFxExtract.fileTextboxTitle', 'File Location'),
|
||||||
|
actions: [this.fileButton]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createVersionTextBox(): Promise<sqlops.FormComponent> {
|
||||||
|
this.versionTextBox = this.view.modelBuilder.inputBox().withProperties({
|
||||||
|
required: true
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
// default filepath
|
||||||
|
this.versionTextBox.value = '1.0.0.0';
|
||||||
|
this.model.version = this.versionTextBox.value;
|
||||||
|
|
||||||
|
this.versionTextBox.onTextChanged(async () => {
|
||||||
|
this.model.version = this.versionTextBox.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.versionTextBox,
|
||||||
|
title: localize('dacFxExtract.versionTextboxTitle', 'Version (use x.x.x.x where x is a number)'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,57 +102,9 @@ export class FileConfigPage extends ImportPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async populateServerDropdown(): Promise<boolean> {
|
private async populateServerDropdown(): Promise<boolean> {
|
||||||
let cons = await sqlops.connection.getActiveConnections();
|
let values = await this.getServerValues();
|
||||||
// This user has no active connections ABORT MISSION
|
if (values === undefined) {
|
||||||
if (!cons || cons.length === 0) {
|
return false;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let count = -1;
|
|
||||||
let idx = -1;
|
|
||||||
|
|
||||||
|
|
||||||
let values = cons.map(c => {
|
|
||||||
// Handle the code to remember what the user's choice was from before
|
|
||||||
count++;
|
|
||||||
if (idx === -1) {
|
|
||||||
if (this.model.server && c.connectionId === this.model.server.connectionId) {
|
|
||||||
idx = count;
|
|
||||||
} else if (this.model.serverId && c.connectionId === this.model.serverId) {
|
|
||||||
idx = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let db = c.options.databaseDisplayName;
|
|
||||||
let usr = c.options.user;
|
|
||||||
let srv = c.options.server;
|
|
||||||
|
|
||||||
if (!db) {
|
|
||||||
db = '<default>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!usr) {
|
|
||||||
usr = 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
let finalName = `${srv}, ${db} (${usr})`;
|
|
||||||
return {
|
|
||||||
connection: c,
|
|
||||||
displayName: finalName,
|
|
||||||
name: c.connectionId
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (idx >= 0) {
|
|
||||||
let tmp = values[0];
|
|
||||||
values[0] = values[idx];
|
|
||||||
values[idx] = tmp;
|
|
||||||
} else {
|
|
||||||
delete this.model.server;
|
|
||||||
delete this.model.serverId;
|
|
||||||
delete this.model.database;
|
|
||||||
delete this.model.schema;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.model.server = values[0].connection;
|
this.model.server = values[0].connection;
|
||||||
@@ -195,29 +147,7 @@ export class FileConfigPage extends ImportPage {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let values = await this.getDatabaseValues();
|
||||||
let idx = -1;
|
|
||||||
let count = -1;
|
|
||||||
let values = (await sqlops.connection.listDatabases(this.model.server.connectionId)).map(db => {
|
|
||||||
count++;
|
|
||||||
if (this.model.database && db === this.model.database) {
|
|
||||||
idx = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
displayName: db,
|
|
||||||
name: db
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (idx >= 0) {
|
|
||||||
let tmp = values[0];
|
|
||||||
values[0] = values[idx];
|
|
||||||
values[idx] = tmp;
|
|
||||||
} else {
|
|
||||||
delete this.model.database;
|
|
||||||
delete this.model.schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.model.database = values[0].name;
|
this.model.database = values[0].name;
|
||||||
|
|
||||||
@@ -377,6 +307,18 @@ export class FileConfigPage extends ImportPage {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected deleteServerValues() {
|
||||||
|
delete this.model.server;
|
||||||
|
delete this.model.serverId;
|
||||||
|
delete this.model.database;
|
||||||
|
delete this.model.schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected deleteDatabaseValues() {
|
||||||
|
delete this.model.database;
|
||||||
|
delete this.model.schema;
|
||||||
|
}
|
||||||
|
|
||||||
// private async populateTableNames(): Promise<boolean> {
|
// private async populateTableNames(): Promise<boolean> {
|
||||||
// this.tableNames = [];
|
// this.tableNames = [];
|
||||||
// let databaseName = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
// let databaseName = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
||||||
|
|||||||
101
extensions/import/src/wizard/pages/importConfigPage.ts
Normal file
101
extensions/import/src/wizard/pages/importConfigPage.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { DacFxDataModel } from '../api/models';
|
||||||
|
import { DataTierApplicationWizard } from '../dataTierApplicationWizard';
|
||||||
|
import { DacFxConfigPage } from '../api/dacFxConfigPage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class ImportConfigPage extends DacFxConfigPage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
|
||||||
|
private form: sqlops.FormContainer;
|
||||||
|
|
||||||
|
public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) {
|
||||||
|
super(instance, wizardPage, model, view);
|
||||||
|
this.fileExtension = '.bacpac';
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
let databaseComponent = await this.createDatabaseTextBox();
|
||||||
|
let serverComponent = await this.createServerDropdown(true);
|
||||||
|
let fileBrowserComponent = await this.createFileBrowser();
|
||||||
|
|
||||||
|
this.form = this.view.modelBuilder.formContainer()
|
||||||
|
.withFormItems(
|
||||||
|
[
|
||||||
|
fileBrowserComponent,
|
||||||
|
serverComponent,
|
||||||
|
databaseComponent,
|
||||||
|
], {
|
||||||
|
horizontal: true,
|
||||||
|
componentWidth: 400
|
||||||
|
}).component();
|
||||||
|
await this.view.initializeModel(this.form);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPageEnter(): Promise<boolean> {
|
||||||
|
let r1 = await this.populateServerDropdown();
|
||||||
|
return r1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createFileBrowser(): Promise<sqlops.FormComponent> {
|
||||||
|
this.createFileBrowserParts();
|
||||||
|
|
||||||
|
this.fileButton.onDidClick(async (click) => {
|
||||||
|
let fileUris = await vscode.window.showOpenDialog(
|
||||||
|
{
|
||||||
|
canSelectFiles: true,
|
||||||
|
canSelectFolders: false,
|
||||||
|
canSelectMany: false,
|
||||||
|
defaultUri: vscode.Uri.file(os.homedir()),
|
||||||
|
openLabel: localize('dacFxImport.openFile', 'Open'),
|
||||||
|
filters: {
|
||||||
|
'bacpac Files': ['bacpac'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fileUris || fileUris.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileUri = fileUris[0];
|
||||||
|
this.fileTextBox.value = fileUri.fsPath;
|
||||||
|
this.model.filePath = fileUri.fsPath;
|
||||||
|
this.model.database = this.generateDatabaseName(this.model.filePath);
|
||||||
|
this.databaseTextBox.value = this.model.database;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fileTextBox.onTextChanged(async () => {
|
||||||
|
this.model.filePath = this.fileTextBox.value;
|
||||||
|
this.model.database = this.generateDatabaseName(this.model.filePath);
|
||||||
|
this.databaseTextBox.value = this.model.database;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.fileTextBox,
|
||||||
|
title: localize('dacFxImport.fileTextboxTitle', 'File Location'),
|
||||||
|
actions: [this.fileButton]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateDatabaseName(filePath: string): string {
|
||||||
|
let result = path.parse(filePath);
|
||||||
|
return result.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
174
extensions/import/src/wizard/pages/selectOperationpage.ts
Normal file
174
extensions/import/src/wizard/pages/selectOperationpage.ts
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import { DacFxDataModel } from '../api/models';
|
||||||
|
import { DataTierApplicationWizard, Operation } from '../DataTierApplicationWizard';
|
||||||
|
import { BasePage } from '../api/basePage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class SelectOperationPage extends BasePage {
|
||||||
|
|
||||||
|
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
protected readonly instance: DataTierApplicationWizard;
|
||||||
|
protected readonly model: DacFxDataModel;
|
||||||
|
protected readonly view: sqlops.ModelView;
|
||||||
|
|
||||||
|
private deployRadioButton: sqlops.RadioButtonComponent;
|
||||||
|
private extractRadioButton: sqlops.RadioButtonComponent;
|
||||||
|
private importRadioButton: sqlops.RadioButtonComponent;
|
||||||
|
private exportRadioButton: sqlops.RadioButtonComponent;
|
||||||
|
private form: sqlops.FormContainer;
|
||||||
|
|
||||||
|
public constructor(instance: DataTierApplicationWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: DacFxDataModel, view: sqlops.ModelView) {
|
||||||
|
super();
|
||||||
|
this.instance = instance;
|
||||||
|
this.wizardPage = wizardPage;
|
||||||
|
this.model = model;
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
let deployComponent = await this.createDeployRadioButton();
|
||||||
|
let extractComponent = await this.createExtractRadioButton();
|
||||||
|
let importComponent = await this.createImportRadioButton();
|
||||||
|
let exportComponent = await this.createExportRadioButton();
|
||||||
|
|
||||||
|
this.form = this.view.modelBuilder.formContainer()
|
||||||
|
.withFormItems(
|
||||||
|
[
|
||||||
|
deployComponent,
|
||||||
|
extractComponent,
|
||||||
|
importComponent,
|
||||||
|
exportComponent
|
||||||
|
], {
|
||||||
|
horizontal: true
|
||||||
|
}).component();
|
||||||
|
await this.view.initializeModel(this.form);
|
||||||
|
|
||||||
|
// default have the first radio button checked
|
||||||
|
this.deployRadioButton.checked = true;
|
||||||
|
this.instance.setDoneButton(Operation.deploy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onPageEnter(): Promise<boolean> {
|
||||||
|
let numPages = this.instance.wizard.pages.length;
|
||||||
|
for (let i = numPages - 1; i > 2; --i) {
|
||||||
|
await this.instance.wizard.removePage(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createDeployRadioButton(): Promise<sqlops.FormComponent> {
|
||||||
|
this.deployRadioButton = this.view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
name: 'selectedOperation',
|
||||||
|
label: localize('dacFx.deployRadioButtonLabel', 'Deploy a data-tier application .dacpac file to an instance of SQL Server [Deploy Dacpac]'),
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.deployRadioButton.onDidClick(() => {
|
||||||
|
// remove the previous page
|
||||||
|
this.instance.wizard.removePage(1);
|
||||||
|
|
||||||
|
// add deploy page
|
||||||
|
let page = this.instance.pages.get('deployConfig');
|
||||||
|
this.instance.wizard.addPage(page.wizardPage, 1);
|
||||||
|
|
||||||
|
// change button text and operation
|
||||||
|
this.instance.setDoneButton(Operation.deploy);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.deployRadioButton,
|
||||||
|
title: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createExtractRadioButton(): Promise<sqlops.FormComponent> {
|
||||||
|
this.extractRadioButton = this.view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
name: 'selectedOperation',
|
||||||
|
label: localize('dacFx.extractRadioButtonLabel', 'Extract a data-tier application from an instance of SQL Server to a .dacpac file [Extract Dacpac]'),
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.extractRadioButton.onDidClick(() => {
|
||||||
|
// remove the previous pages
|
||||||
|
this.instance.wizard.removePage(1);
|
||||||
|
|
||||||
|
// add the extract page
|
||||||
|
let page = this.instance.pages.get('extractConfig');
|
||||||
|
this.instance.wizard.addPage(page.wizardPage, 1);
|
||||||
|
|
||||||
|
// change button text and operation
|
||||||
|
this.instance.setDoneButton(Operation.extract);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.extractRadioButton,
|
||||||
|
title: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createImportRadioButton(): Promise<sqlops.FormComponent> {
|
||||||
|
this.importRadioButton = this.view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
name: 'selectedOperation',
|
||||||
|
label: localize('dacFx.importRadioButtonLabel', 'Create a database from a .bacpac file [Import Bacpac]'),
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.importRadioButton.onDidClick(() => {
|
||||||
|
// remove the previous page
|
||||||
|
this.instance.wizard.removePage(1);
|
||||||
|
|
||||||
|
// add the import page
|
||||||
|
let page = this.instance.pages.get('importConfig');
|
||||||
|
this.instance.wizard.addPage(page.wizardPage, 1);
|
||||||
|
|
||||||
|
// change button text and operation
|
||||||
|
this.instance.setDoneButton(Operation.import);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.importRadioButton,
|
||||||
|
title: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createExportRadioButton(): Promise<sqlops.FormComponent> {
|
||||||
|
this.exportRadioButton = this.view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
name: 'selectedOperation',
|
||||||
|
label: localize('dacFx.exportRadioButtonLabel', 'Export the schema and data from a database to the logical .bacpac file format [Export Bacpac]'),
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.exportRadioButton.onDidClick(() => {
|
||||||
|
// remove the 2 previous pages
|
||||||
|
this.instance.wizard.removePage(1);
|
||||||
|
|
||||||
|
// add the export pages
|
||||||
|
let page = this.instance.pages.get('exportConfig');
|
||||||
|
this.instance.wizard.addPage(page.wizardPage, 1);
|
||||||
|
|
||||||
|
// change button text and operation
|
||||||
|
this.instance.setDoneButton(Operation.export);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
component: this.exportRadioButton,
|
||||||
|
title: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public setupNavigationValidator() {
|
||||||
|
this.instance.registerNavigationValidator(() => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -291,3 +291,60 @@ export namespace DeleteAgentJobScheduleRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------- < Agent Management > ------------------------------------
|
// ------------------------------- < Agent Management > ------------------------------------
|
||||||
|
|
||||||
|
// ------------------------------- < DacFx > ------------------------------------
|
||||||
|
|
||||||
|
export enum TaskExecutionMode {
|
||||||
|
execute = 0,
|
||||||
|
script = 1,
|
||||||
|
executeAndScript = 2,
|
||||||
|
}
|
||||||
|
export interface ExportParams {
|
||||||
|
databaseName: string;
|
||||||
|
packageFilePath: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImportParams {
|
||||||
|
packageFilePath: string;
|
||||||
|
databaseName: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface ExtractParams {
|
||||||
|
databaseName: string;
|
||||||
|
packageFilePath: string;
|
||||||
|
applicationName: string;
|
||||||
|
applicationVersion: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeployParams {
|
||||||
|
packageFilePath: string;
|
||||||
|
databaseName: string;
|
||||||
|
upgradeExisting: boolean;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace ExportRequest {
|
||||||
|
export const type = new RequestType<ExportParams, sqlops.DacFxResult, void, void>('dacfx/export');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace ImportRequest {
|
||||||
|
export const type = new RequestType<ImportParams, sqlops.DacFxResult, void, void>('dacfx/import');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace ExtractRequest {
|
||||||
|
export const type = new RequestType<ExtractParams, sqlops.DacFxResult, void, void>('dacfx/extract');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DeployRequest {
|
||||||
|
export const type = new RequestType<DeployParams, sqlops.DacFxResult, void, void>('dacfx/deploy');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------- < DacFx > ------------------------------------
|
||||||
@@ -28,6 +28,94 @@ export class TelemetryFeature implements StaticFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class DacFxServicesFeature extends SqlOpsFeature<undefined> {
|
||||||
|
private static readonly messageTypes: RPCMessageType[] = [
|
||||||
|
contracts.ExportRequest.type,
|
||||||
|
contracts.ImportRequest.type,
|
||||||
|
contracts.ExtractRequest.type,
|
||||||
|
contracts.DeployRequest.type
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(client: SqlOpsDataClient) {
|
||||||
|
super(client, DacFxServicesFeature.messageTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize(capabilities: ServerCapabilities): void {
|
||||||
|
this.register(this.messages, {
|
||||||
|
id: UUID.generateUuid(),
|
||||||
|
registerOptions: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected registerProvider(options: undefined): Disposable {
|
||||||
|
const client = this._client;
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
let exportBacpac = (databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> => {
|
||||||
|
let params: contracts.ExportParams = { databaseName: databaseName, packageFilePath: packageFilePath, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
|
||||||
|
return client.sendRequest(contracts.ExportRequest.type, params).then(
|
||||||
|
r => {
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(contracts.ExportRequest.type, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let importBacpac = (packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> => {
|
||||||
|
let params: contracts.ImportParams = { packageFilePath: packageFilePath, databaseName: databaseName, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
|
||||||
|
return client.sendRequest(contracts.ImportRequest.type, params).then(
|
||||||
|
r => {
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(contracts.ImportRequest.type, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let extractDacpac = (databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> => {
|
||||||
|
let params: contracts.ExtractParams = { databaseName: databaseName, packageFilePath: packageFilePath, applicationName: applicationName, applicationVersion: applicationVersion, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
|
||||||
|
return client.sendRequest(contracts.ExtractRequest.type, params).then(
|
||||||
|
r => {
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(contracts.ExtractRequest.type, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let deployDacpac = (packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> => {
|
||||||
|
let params: contracts.DeployParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, upgradeExisting: upgradeExisting, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode };
|
||||||
|
return client.sendRequest(contracts.DeployRequest.type, params).then(
|
||||||
|
r => {
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
client.logFailedRequest(contracts.DeployRequest.type, e);
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return sqlops.dataprotocol.registerDacFxServicesProvider({
|
||||||
|
providerId: client.providerId,
|
||||||
|
exportBacpac,
|
||||||
|
importBacpac,
|
||||||
|
extractDacpac,
|
||||||
|
deployDacpac
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
||||||
private static readonly messagesTypes: RPCMessageType[] = [
|
private static readonly messagesTypes: RPCMessageType[] = [
|
||||||
contracts.AgentJobsRequest.type,
|
contracts.AgentJobsRequest.type,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { CredentialStore } from './credentialstore/credentialstore';
|
|||||||
import { AzureResourceProvider } from './resourceProvider/resourceProvider';
|
import { AzureResourceProvider } from './resourceProvider/resourceProvider';
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
|
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
|
||||||
import { TelemetryFeature, AgentServicesFeature } from './features';
|
import { TelemetryFeature, AgentServicesFeature, DacFxServicesFeature } from './features';
|
||||||
|
|
||||||
const baseConfig = require('./config.json');
|
const baseConfig = require('./config.json');
|
||||||
const outputChannel = vscode.window.createOutputChannel(Constants.serviceName);
|
const outputChannel = vscode.window.createOutputChannel(Constants.serviceName);
|
||||||
@@ -55,7 +55,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
// we only want to add new features
|
// we only want to add new features
|
||||||
...SqlOpsDataClient.defaultFeatures,
|
...SqlOpsDataClient.defaultFeatures,
|
||||||
TelemetryFeature,
|
TelemetryFeature,
|
||||||
AgentServicesFeature
|
AgentServicesFeature,
|
||||||
|
DacFxServicesFeature,
|
||||||
],
|
],
|
||||||
outputChannel: new CustomOutputChannel()
|
outputChannel: new CustomOutputChannel()
|
||||||
};
|
};
|
||||||
|
|||||||
78
src/sql/services/dacfx/dacFxService.ts
Normal file
78
src/sql/services/dacfx/dacFxService.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 sqlops from 'sqlops';
|
||||||
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||||
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
import { data } from 'vs/base/test/common/filters.perf.data';
|
||||||
|
|
||||||
|
export const SERVICE_ID = 'dacFxService';
|
||||||
|
export const IDacFxService = createDecorator<IDacFxService>(SERVICE_ID);
|
||||||
|
|
||||||
|
export interface IDacFxService {
|
||||||
|
_serviceBrand: any;
|
||||||
|
|
||||||
|
registerProvider(providerId: string, provider: sqlops.DacFxServicesProvider): void;
|
||||||
|
exportBacpac(sourceDatabaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
|
importBacpac(packageFilePath: string, targetDatabaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
|
extractDacpac(sourceDatabaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
|
deployDacpac(packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DacFxService implements IDacFxService {
|
||||||
|
_serviceBrand: any;
|
||||||
|
private _providers: { [handle: string]: sqlops.DacFxServicesProvider; } = Object.create(null);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@IConnectionManagementService private _connectionService: IConnectionManagementService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
registerProvider(providerId: string, provider: sqlops.DacFxServicesProvider): void {
|
||||||
|
this._providers[providerId] = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportBacpac(databasesName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return this._runAction(ownerUri, (runner) => {
|
||||||
|
return runner.exportBacpac(databasesName, packageFilePath, ownerUri, taskExecutionMode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return this._runAction(ownerUri, (runner) => {
|
||||||
|
return runner.importBacpac(packageFilePath, databaseName, ownerUri, taskExecutionMode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return this._runAction(ownerUri, (runner) => {
|
||||||
|
return runner.extractDacpac(databaseName, packageFilePath, applicationName, applicationVersion, ownerUri, taskExecutionMode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return this._runAction(ownerUri, (runner) => {
|
||||||
|
return runner.deployDacpac(packageFilePath, databaseName, upgradeExisting, ownerUri, taskExecutionMode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _runAction<T>(uri: string, action: (handler: sqlops.DacFxServicesProvider) => Thenable<T>): Thenable<T> {
|
||||||
|
let providerId: string = this._connectionService.getProviderIdFromUri(uri);
|
||||||
|
|
||||||
|
if (!providerId) {
|
||||||
|
return TPromise.wrapError(new Error(localize('providerIdNotValidError', "Connection is required in order to interact with DacFxService")));
|
||||||
|
}
|
||||||
|
let handler = this._providers[providerId];
|
||||||
|
if (handler) {
|
||||||
|
return action(handler);
|
||||||
|
} else {
|
||||||
|
return TPromise.wrapError(new Error(localize('noHandlerRegistered', "No Handler Registered")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/sql/sqlops.d.ts
vendored
45
src/sql/sqlops.d.ts
vendored
@@ -37,6 +37,8 @@ declare module 'sqlops' {
|
|||||||
|
|
||||||
export function registerCapabilitiesServiceProvider(provider: CapabilitiesProvider): vscode.Disposable;
|
export function registerCapabilitiesServiceProvider(provider: CapabilitiesProvider): vscode.Disposable;
|
||||||
|
|
||||||
|
export function registerDacFxServicesProvider(provider: DacFxServicesProvider): vscode.Disposable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An [event](#Event) which fires when the specific flavor of a language used in DMP
|
* An [event](#Event) which fires when the specific flavor of a language used in DMP
|
||||||
* connections has changed. And example is for a SQL connection, the flavor changes
|
* connections has changed. And example is for a SQL connection, the flavor changes
|
||||||
@@ -1583,6 +1585,49 @@ declare module 'sqlops' {
|
|||||||
registerOnUpdated(handler: () => any): void;
|
registerOnUpdated(handler: () => any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DacFx interfaces -----------------------------------------------------------------------
|
||||||
|
export interface DacFxResult extends ResultStatus {
|
||||||
|
operationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExportParams {
|
||||||
|
databaseName: string;
|
||||||
|
packageFilePath: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImportParams {
|
||||||
|
packageFilePath: string;
|
||||||
|
databaseName: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExtractParams {
|
||||||
|
databaseName: string;
|
||||||
|
packageFilePath: string;
|
||||||
|
applicationName: string;
|
||||||
|
applicationVersion: string;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeployParams {
|
||||||
|
packageFilePath: string;
|
||||||
|
databaseName: string;
|
||||||
|
upgradeExisting: boolean;
|
||||||
|
ownerUri: string;
|
||||||
|
taskExecutionMode: TaskExecutionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DacFxServicesProvider extends DataProvider {
|
||||||
|
exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
|
importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
|
extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
|
deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable<DacFxResult>;
|
||||||
|
}
|
||||||
|
|
||||||
// Security service interfaces ------------------------------------------------------------------------
|
// Security service interfaces ------------------------------------------------------------------------
|
||||||
export interface CredentialInfo {
|
export interface CredentialInfo {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
3
src/sql/sqlops.proposed.d.ts
vendored
3
src/sql/sqlops.proposed.d.ts
vendored
@@ -1232,7 +1232,8 @@ declare module 'sqlops' {
|
|||||||
QueryProvider = 'QueryProvider',
|
QueryProvider = 'QueryProvider',
|
||||||
AdminServicesProvider = 'AdminServicesProvider',
|
AdminServicesProvider = 'AdminServicesProvider',
|
||||||
AgentServicesProvider = 'AgentServicesProvider',
|
AgentServicesProvider = 'AgentServicesProvider',
|
||||||
CapabilitiesProvider = 'CapabilitiesProvider'
|
CapabilitiesProvider = 'CapabilitiesProvider',
|
||||||
|
DacFxServicesProvider = 'DacFxServicesProvider',
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace dataprotocol {
|
export namespace dataprotocol {
|
||||||
|
|||||||
@@ -285,7 +285,8 @@ export enum DataProviderType {
|
|||||||
QueryProvider = 'QueryProvider',
|
QueryProvider = 'QueryProvider',
|
||||||
AdminServicesProvider = 'AdminServicesProvider',
|
AdminServicesProvider = 'AdminServicesProvider',
|
||||||
AgentServicesProvider = 'AgentServicesProvider',
|
AgentServicesProvider = 'AgentServicesProvider',
|
||||||
CapabilitiesProvider = 'CapabilitiesProvider'
|
CapabilitiesProvider = 'CapabilitiesProvider',
|
||||||
|
DacFxServicesProvider = 'DacFxServicesProvider',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DeclarativeDataType {
|
export enum DeclarativeDataType {
|
||||||
|
|||||||
@@ -156,6 +156,12 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
|||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$registerDacFxServiceProvider(provider: sqlops.DacFxServicesProvider): vscode.Disposable {
|
||||||
|
let rt = this.registerProvider(provider, DataProviderType.DacFxServicesProvider);
|
||||||
|
this._proxy.$registerDacFxServicesProvider(provider.providerId, provider.handle);
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
|
||||||
// Capabilities Discovery handlers
|
// Capabilities Discovery handlers
|
||||||
$getServerCapabilities(handle: number, client: sqlops.DataProtocolClientCapabilities): Thenable<sqlops.DataProtocolServerCapabilities> {
|
$getServerCapabilities(handle: number, client: sqlops.DataProtocolClientCapabilities): Thenable<sqlops.DataProtocolServerCapabilities> {
|
||||||
return this._resolveProvider<sqlops.CapabilitiesProvider>(handle).getServerCapabilities(client);
|
return this._resolveProvider<sqlops.CapabilitiesProvider>(handle).getServerCapabilities(client);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { IProfilerService } from 'sql/parts/profiler/service/interfaces';
|
|||||||
import { ISerializationService } from 'sql/services/serialization/serializationService';
|
import { ISerializationService } from 'sql/services/serialization/serializationService';
|
||||||
import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces';
|
import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces';
|
||||||
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import { IDacFxService } from 'sql/services/dacfx/dacFxService';
|
||||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +56,8 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
|||||||
@ITaskService private _taskService: ITaskService,
|
@ITaskService private _taskService: ITaskService,
|
||||||
@IProfilerService private _profilerService: IProfilerService,
|
@IProfilerService private _profilerService: IProfilerService,
|
||||||
@ISerializationService private _serializationService: ISerializationService,
|
@ISerializationService private _serializationService: ISerializationService,
|
||||||
@IFileBrowserService private _fileBrowserService: IFileBrowserService
|
@IFileBrowserService private _fileBrowserService: IFileBrowserService,
|
||||||
|
@IDacFxService private _dacFxService: IDacFxService,
|
||||||
) {
|
) {
|
||||||
if (extHostContext) {
|
if (extHostContext) {
|
||||||
this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostDataProtocol);
|
this._proxy = extHostContext.getProxy(SqlExtHostContext.ExtHostDataProtocol);
|
||||||
@@ -399,6 +401,26 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public $registerDacFxServicesProvider(providerId: string, handle: number): TPromise<any> {
|
||||||
|
const self = this;
|
||||||
|
this._dacFxService.registerProvider(providerId, <sqlops.DacFxServicesProvider>{
|
||||||
|
exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return self._proxy.$exportBacpac(handle, databaseName, packageFilePath, ownerUri, taskExecutionMode);
|
||||||
|
},
|
||||||
|
importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return self._proxy.$importBacpac(handle, packageFilePath, databaseName, ownerUri, taskExecutionMode);
|
||||||
|
},
|
||||||
|
extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return self._proxy.$extractDacpac(handle, databaseName, packageFilePath, applicationName, applicationVersion, ownerUri, taskExecutionMode);
|
||||||
|
},
|
||||||
|
deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
|
||||||
|
return self._proxy.$deployDacpac(handle, packageFilePath, databaseName, upgradeExisting, ownerUri, taskExecutionMode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// Connection Management handlers
|
// Connection Management handlers
|
||||||
public $onConnectionComplete(handle: number, connectionInfoSummary: sqlops.ConnectionInfoSummary): void {
|
public $onConnectionComplete(handle: number, connectionInfoSummary: sqlops.ConnectionInfoSummary): void {
|
||||||
this._connectionManagementService.onConnectionComplete(handle, connectionInfoSummary);
|
this._connectionManagementService.onConnectionComplete(handle, connectionInfoSummary);
|
||||||
|
|||||||
@@ -310,6 +310,10 @@ export function createApiFactory(
|
|||||||
return extHostDataProvider.$registerAgentServiceProvider(provider);
|
return extHostDataProvider.$registerAgentServiceProvider(provider);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let registerDacFxServicesProvider = (provider: sqlops.DacFxServicesProvider): vscode.Disposable => {
|
||||||
|
return extHostDataProvider.$registerDacFxServiceProvider(provider);
|
||||||
|
};
|
||||||
|
|
||||||
// namespace: dataprotocol
|
// namespace: dataprotocol
|
||||||
const dataprotocol: typeof sqlops.dataprotocol = {
|
const dataprotocol: typeof sqlops.dataprotocol = {
|
||||||
registerBackupProvider,
|
registerBackupProvider,
|
||||||
@@ -325,6 +329,7 @@ export function createApiFactory(
|
|||||||
registerAdminServicesProvider,
|
registerAdminServicesProvider,
|
||||||
registerAgentServicesProvider,
|
registerAgentServicesProvider,
|
||||||
registerCapabilitiesServiceProvider,
|
registerCapabilitiesServiceProvider,
|
||||||
|
registerDacFxServicesProvider,
|
||||||
onDidChangeLanguageFlavor(listener: (e: sqlops.DidChangeLanguageFlavorParams) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
|
onDidChangeLanguageFlavor(listener: (e: sqlops.DidChangeLanguageFlavorParams) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
|
||||||
return extHostDataProvider.onDidChangeLanguageFlavor(listener, thisArgs, disposables);
|
return extHostDataProvider.onDidChangeLanguageFlavor(listener, thisArgs, disposables);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -409,6 +409,26 @@ export abstract class ExtHostDataProtocolShape {
|
|||||||
* Get Agent Credentials list
|
* Get Agent Credentials list
|
||||||
*/
|
*/
|
||||||
$getCredentials(handle: number, connectionUri: string): Thenable<sqlops.GetCredentialsResult> { throw ni(); }
|
$getCredentials(handle: number, connectionUri: string): Thenable<sqlops.GetCredentialsResult> { throw ni(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DacFx export bacpac
|
||||||
|
*/
|
||||||
|
$exportBacpac(handle: number, databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> { throw ni(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DacFx import bacpac
|
||||||
|
*/
|
||||||
|
$importBacpac(handle: number, packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> { throw ni(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DacFx extract dacpac
|
||||||
|
*/
|
||||||
|
$extractDacpac(handle: number, databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> { throw ni(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DacFx deploy dacpac
|
||||||
|
*/
|
||||||
|
$deployDacpac(handle: number, packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> { throw ni(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -476,6 +496,7 @@ export interface MainThreadDataProtocolShape extends IDisposable {
|
|||||||
$registerCapabilitiesServiceProvider(providerId: string, handle: number): TPromise<any>;
|
$registerCapabilitiesServiceProvider(providerId: string, handle: number): TPromise<any>;
|
||||||
$registerAdminServicesProvider(providerId: string, handle: number): TPromise<any>;
|
$registerAdminServicesProvider(providerId: string, handle: number): TPromise<any>;
|
||||||
$registerAgentServicesProvider(providerId: string, handle: number): TPromise<any>;
|
$registerAgentServicesProvider(providerId: string, handle: number): TPromise<any>;
|
||||||
|
$registerDacFxServicesProvider(providerId: string, handle: number): TPromise<any>;
|
||||||
$unregisterProvider(handle: number): TPromise<any>;
|
$unregisterProvider(handle: number): TPromise<any>;
|
||||||
$onConnectionComplete(handle: number, connectionInfoSummary: sqlops.ConnectionInfoSummary): void;
|
$onConnectionComplete(handle: number, connectionInfoSummary: sqlops.ConnectionInfoSummary): void;
|
||||||
$onIntelliSenseCacheComplete(handle: number, connectionUri: string): void;
|
$onIntelliSenseCacheComplete(handle: number, connectionUri: string): void;
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ import { IScriptingService, ScriptingService } from 'sql/services/scripting/scri
|
|||||||
import { IAdminService, AdminService } from 'sql/parts/admin/common/adminService';
|
import { IAdminService, AdminService } from 'sql/parts/admin/common/adminService';
|
||||||
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
||||||
import { JobManagementService } from 'sql/parts/jobManagement/common/jobManagementService';
|
import { JobManagementService } from 'sql/parts/jobManagement/common/jobManagementService';
|
||||||
|
import { IDacFxService, DacFxService } from 'sql/services/dacfx/dacFxService';
|
||||||
import { IBackupService, IBackupUiService } from 'sql/parts/disasterRecovery/backup/common/backupService';
|
import { IBackupService, IBackupUiService } from 'sql/parts/disasterRecovery/backup/common/backupService';
|
||||||
import { BackupService, BackupUiService } from 'sql/parts/disasterRecovery/backup/common/backupServiceImp';
|
import { BackupService, BackupUiService } from 'sql/parts/disasterRecovery/backup/common/backupServiceImp';
|
||||||
import { IRestoreDialogController, IRestoreService } from 'sql/parts/disasterRecovery/restore/common/restoreService';
|
import { IRestoreDialogController, IRestoreService } from 'sql/parts/disasterRecovery/restore/common/restoreService';
|
||||||
@@ -586,6 +587,8 @@ export class Workbench extends Disposable implements IPartService {
|
|||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
serviceCollection.set(ICommandLineProcessing, this.instantiationService.createInstance(CommandLineService));
|
serviceCollection.set(ICommandLineProcessing, this.instantiationService.createInstance(CommandLineService));
|
||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
|
serviceCollection.set(IDacFxService, this.instantiationService.createInstance(DacFxService));
|
||||||
|
|
||||||
this._register(toDisposable(() => connectionManagementService.shutdown()));
|
this._register(toDisposable(() => connectionManagementService.shutdown()));
|
||||||
this._register(toDisposable(() => accountManagementService.shutdown()));
|
this._register(toDisposable(() => accountManagementService.shutdown()));
|
||||||
this._register(toDisposable(() => notebookService.shutdown()));
|
this._register(toDisposable(() => notebookService.shutdown()));
|
||||||
|
|||||||
Reference in New Issue
Block a user