mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Load profile support for sql project deploy (#10948)
* load database name from profile.xml * load sqlcmd variables from profile * Add warning text * add tests * fix file filter for windows * add comments * show SQLCMD variables in a table * reset dialog before testing readPublishProfile callback
This commit is contained in:
@@ -41,6 +41,7 @@ export const databaseReferenceSameDatabase = localize('databaseReferenceSameData
|
|||||||
export const databaseReferenceDifferentDabaseSameServer = localize('databaseReferenceDifferentDabaseSameServer', "Different database, same server");
|
export const databaseReferenceDifferentDabaseSameServer = localize('databaseReferenceDifferentDabaseSameServer', "Different database, same server");
|
||||||
export const databaseReferenceDatabaseName = localize('databaseReferenceDatabaseName', "Database name");
|
export const databaseReferenceDatabaseName = localize('databaseReferenceDatabaseName', "Database name");
|
||||||
export const dacpacFiles = localize('dacpacFiles', "dacpac Files");
|
export const dacpacFiles = localize('dacpacFiles', "dacpac Files");
|
||||||
|
export const publishSettingsFiles = localize('publishSettingsFiles', "Publish Settings File");
|
||||||
export const systemDatabase = localize('systemDatabase', "System Database");
|
export const systemDatabase = localize('systemDatabase', "System Database");
|
||||||
export function newObjectNamePrompt(objectType: string) { return localize('newObjectNamePrompt', 'New {0} name:', objectType); }
|
export function newObjectNamePrompt(objectType: string) { return localize('newObjectNamePrompt', 'New {0} name:', objectType); }
|
||||||
|
|
||||||
@@ -61,6 +62,11 @@ export const connectionRadioButtonLabel = localize('connectionRadioButtonLabel',
|
|||||||
export const selectConnectionRadioButtonsTitle = localize('selectconnectionRadioButtonsTitle', "Specify connection from:");
|
export const selectConnectionRadioButtonsTitle = localize('selectconnectionRadioButtonsTitle', "Specify connection from:");
|
||||||
export const dataSourceDropdownTitle = localize('dataSourceDropdownTitle', "Data source");
|
export const dataSourceDropdownTitle = localize('dataSourceDropdownTitle', "Data source");
|
||||||
export const noDataSourcesText = localize('noDataSourcesText', "No data sources in this project");
|
export const noDataSourcesText = localize('noDataSourcesText', "No data sources in this project");
|
||||||
|
export const loadProfileButtonText = localize('loadProfileButtonText', "Load Profile...");
|
||||||
|
export const profileWarningText = localize('profileWarningText', "⚠Warning: Only database name and SQLCMD variables are able to be loaded from a profile at this time");
|
||||||
|
export const sqlCmdTableLabel = localize('sqlCmdTableLabel', "SQLCMD Variables");
|
||||||
|
export const sqlCmdVariableColumn = localize('sqlCmdVariableColumn', "Variable");
|
||||||
|
export const sqlCmdValueColumn = localize('sqlCmdValueColumn', "Value");
|
||||||
|
|
||||||
// Error messages
|
// Error messages
|
||||||
|
|
||||||
@@ -146,6 +152,9 @@ export const NETFrameworkAssembly = 'Microsoft.NETFramework.ReferenceAssemblies'
|
|||||||
export const VersionNumber = '1.0.0';
|
export const VersionNumber = '1.0.0';
|
||||||
export const All = 'All';
|
export const All = 'All';
|
||||||
|
|
||||||
|
// Profile XML names
|
||||||
|
export const targetDatabaseName = 'TargetDatabaseName';
|
||||||
|
|
||||||
// SQL connection string components
|
// SQL connection string components
|
||||||
export const initialCatalogSetting = 'Initial Catalog';
|
export const initialCatalogSetting = 'Initial Catalog';
|
||||||
export const dataSourceSetting = 'Data Source';
|
export const dataSourceSetting = 'Data Source';
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
|
import * as constants from '../common/constants';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,3 +96,20 @@ export function getSafeNonWindowsPath(filePath: string): string {
|
|||||||
filePath = filePath.split('\\').join('/').split('"').join('');
|
filePath = filePath.split('\\').join('/').split('"').join('');
|
||||||
return '"' + filePath + '"';
|
return '"' + filePath + '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read SQLCMD variables from xmlDoc and return them
|
||||||
|
* @param xmlDoc xml doc to read SQLCMD variables from. Format must be the same that sqlproj and publish profiles use
|
||||||
|
*/
|
||||||
|
export function readSqlCmdVariables(xmlDoc: any): Record<string, string> {
|
||||||
|
let sqlCmdVariables: Record<string, string> = {};
|
||||||
|
for (let i = 0; i < xmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable).length; i++) {
|
||||||
|
const sqlCmdVar = xmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable)[i];
|
||||||
|
const varName = sqlCmdVar.getAttribute(constants.Include);
|
||||||
|
|
||||||
|
const varValue = sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0].childNodes[0].nodeValue;
|
||||||
|
sqlCmdVariables[varName] = varValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqlCmdVariables;
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import * as path from 'path';
|
|||||||
import * as utils from '../common/utils';
|
import * as utils from '../common/utils';
|
||||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||||
import * as templates from '../templates/templates';
|
import * as templates from '../templates/templates';
|
||||||
|
import * as xmldom from 'xmldom';
|
||||||
|
|
||||||
import { Uri, QuickPickItem, WorkspaceFolder, extensions, Extension } from 'vscode';
|
import { Uri, QuickPickItem, WorkspaceFolder, extensions, Extension } from 'vscode';
|
||||||
import { IConnectionProfile, TaskExecutionMode } from 'azdata';
|
import { IConnectionProfile, TaskExecutionMode } from 'azdata';
|
||||||
@@ -19,7 +20,7 @@ import { DeployDatabaseDialog } from '../dialogs/deployDatabaseDialog';
|
|||||||
import { Project, DatabaseReferenceLocation, SystemDatabase, TargetPlatform } from '../models/project';
|
import { Project, DatabaseReferenceLocation, SystemDatabase, TargetPlatform } from '../models/project';
|
||||||
import { SqlDatabaseProjectTreeViewProvider } from './databaseProjectTreeViewProvider';
|
import { SqlDatabaseProjectTreeViewProvider } from './databaseProjectTreeViewProvider';
|
||||||
import { FolderNode } from '../models/tree/fileFolderTreeItem';
|
import { FolderNode } from '../models/tree/fileFolderTreeItem';
|
||||||
import { IDeploymentProfile, IGenerateScriptProfile } from '../models/IDeploymentProfile';
|
import { IDeploymentProfile, IGenerateScriptProfile, PublishSettings } from '../models/IDeploymentProfile';
|
||||||
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
||||||
import { ProjectRootTreeItem } from '../models/tree/projectTreeItem';
|
import { ProjectRootTreeItem } from '../models/tree/projectTreeItem';
|
||||||
import { ImportDataModel } from '../models/api/import';
|
import { ImportDataModel } from '../models/api/import';
|
||||||
@@ -193,6 +194,7 @@ export class ProjectsController {
|
|||||||
|
|
||||||
deployDatabaseDialog.deploy = async (proj, prof) => await this.executionCallback(proj, prof);
|
deployDatabaseDialog.deploy = async (proj, prof) => await this.executionCallback(proj, prof);
|
||||||
deployDatabaseDialog.generateScript = async (proj, prof) => await this.executionCallback(proj, prof);
|
deployDatabaseDialog.generateScript = async (proj, prof) => await this.executionCallback(proj, prof);
|
||||||
|
deployDatabaseDialog.readPublishProfile = async (profileUri) => await this.readPublishProfile(profileUri);
|
||||||
|
|
||||||
deployDatabaseDialog.openDialog();
|
deployDatabaseDialog.openDialog();
|
||||||
|
|
||||||
@@ -216,6 +218,27 @@ export class ProjectsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async readPublishProfile(profileUri: Uri): Promise<PublishSettings> {
|
||||||
|
const profileText = await fs.readFile(profileUri.fsPath);
|
||||||
|
const profileXmlDoc = new xmldom.DOMParser().parseFromString(profileText.toString());
|
||||||
|
|
||||||
|
// read target database name
|
||||||
|
let targetDbName: string = '';
|
||||||
|
let targetDatabaseNameCount = profileXmlDoc.documentElement.getElementsByTagName(constants.targetDatabaseName).length;
|
||||||
|
if (targetDatabaseNameCount > 0) {
|
||||||
|
// if there is more than one TargetDatabaseName nodes, SSDT uses the name in the last one so we'll do the same here
|
||||||
|
targetDbName = profileXmlDoc.documentElement.getElementsByTagName(constants.targetDatabaseName)[targetDatabaseNameCount - 1].textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all SQLCMD variables to include from the profile
|
||||||
|
let sqlCmdVariables = utils.readSqlCmdVariables(profileXmlDoc);
|
||||||
|
|
||||||
|
return {
|
||||||
|
databaseName: targetDbName,
|
||||||
|
sqlCmdVariables: sqlCmdVariables
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async schemaCompare(treeNode: BaseProjectTreeItem): Promise<void> {
|
public async schemaCompare(treeNode: BaseProjectTreeItem): Promise<void> {
|
||||||
// check if schema compare extension is installed
|
// check if schema compare extension is installed
|
||||||
if (this.apiWrapper.getExtension(constants.schemaCompareExtensionId)) {
|
if (this.apiWrapper.getExtension(constants.schemaCompareExtensionId)) {
|
||||||
|
|||||||
@@ -28,15 +28,19 @@ export class DeployDatabaseDialog {
|
|||||||
private targetDatabaseTextBox: azdata.InputBoxComponent | undefined;
|
private targetDatabaseTextBox: azdata.InputBoxComponent | undefined;
|
||||||
private connectionsRadioButton: azdata.RadioButtonComponent | undefined;
|
private connectionsRadioButton: azdata.RadioButtonComponent | undefined;
|
||||||
private dataSourcesRadioButton: azdata.RadioButtonComponent | undefined;
|
private dataSourcesRadioButton: azdata.RadioButtonComponent | undefined;
|
||||||
|
private loadProfileButton: azdata.ButtonComponent | undefined;
|
||||||
|
private sqlCmdVariablesTable: azdata.TableComponent | undefined;
|
||||||
private formBuilder: azdata.FormBuilder | undefined;
|
private formBuilder: azdata.FormBuilder | undefined;
|
||||||
|
|
||||||
private connection: azdata.connection.Connection | undefined;
|
private connection: azdata.connection.Connection | undefined;
|
||||||
private connectionIsDataSource: boolean | undefined;
|
private connectionIsDataSource: boolean | undefined;
|
||||||
|
private profileSqlCmdVars: Record<string, string> | undefined;
|
||||||
|
|
||||||
private toDispose: vscode.Disposable[] = [];
|
private toDispose: vscode.Disposable[] = [];
|
||||||
|
|
||||||
public deploy: ((proj: Project, profile: IDeploymentProfile) => any) | undefined;
|
public deploy: ((proj: Project, profile: IDeploymentProfile) => any) | undefined;
|
||||||
public generateScript: ((proj: Project, profile: IGenerateScriptProfile) => any) | undefined;
|
public generateScript: ((proj: Project, profile: IGenerateScriptProfile) => any) | undefined;
|
||||||
|
public readPublishProfile: ((profileUri: vscode.Uri) => any) | undefined;
|
||||||
|
|
||||||
constructor(private apiWrapper: ApiWrapper, private project: Project) {
|
constructor(private apiWrapper: ApiWrapper, private project: Project) {
|
||||||
this.dialog = azdata.window.createModelViewDialog(constants.deployDialogName);
|
this.dialog = azdata.window.createModelViewDialog(constants.deployDialogName);
|
||||||
@@ -87,6 +91,21 @@ export class DeployDatabaseDialog {
|
|||||||
this.tryEnableGenerateScriptAndOkButtons();
|
this.tryEnableGenerateScriptAndOkButtons();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.loadProfileButton = this.createLoadProfileButton(view);
|
||||||
|
this.sqlCmdVariablesTable = view.modelBuilder.table().withProperties({
|
||||||
|
title: constants.sqlCmdTableLabel,
|
||||||
|
data: this.convertSqlCmdVarsToTableFormat(this.project.sqlCmdVariables),
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
value: constants.sqlCmdVariableColumn
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: constants.sqlCmdValueColumn,
|
||||||
|
}],
|
||||||
|
width: 400,
|
||||||
|
height: 400
|
||||||
|
}).component();
|
||||||
|
|
||||||
this.formBuilder = <azdata.FormBuilder>view.modelBuilder.formContainer()
|
this.formBuilder = <azdata.FormBuilder>view.modelBuilder.formContainer()
|
||||||
.withFormItems([
|
.withFormItems([
|
||||||
{
|
{
|
||||||
@@ -100,6 +119,10 @@ export class DeployDatabaseDialog {
|
|||||||
{
|
{
|
||||||
title: constants.databaseNameLabel,
|
title: constants.databaseNameLabel,
|
||||||
component: this.targetDatabaseTextBox
|
component: this.targetDatabaseTextBox
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: constants.profileWarningText,
|
||||||
|
component: <azdata.ButtonComponent>this.loadProfileButton
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -110,6 +133,15 @@ export class DeployDatabaseDialog {
|
|||||||
width: '100%'
|
width: '100%'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add SQLCMD variables table if the project has any
|
||||||
|
if (Object.keys(this.project.sqlCmdVariables).length > 0) {
|
||||||
|
this.formBuilder.insertFormItem({
|
||||||
|
title: constants.sqlCmdTableLabel,
|
||||||
|
component: <azdata.TableComponent>this.sqlCmdVariablesTable
|
||||||
|
},
|
||||||
|
6);
|
||||||
|
}
|
||||||
|
|
||||||
let formModel = this.formBuilder.component();
|
let formModel = this.formBuilder.component();
|
||||||
await view.initializeModel(formModel);
|
await view.initializeModel(formModel);
|
||||||
});
|
});
|
||||||
@@ -160,11 +192,12 @@ export class DeployDatabaseDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async deployClick(): Promise<void> {
|
public async deployClick(): Promise<void> {
|
||||||
|
const sqlCmdVars = this.getSqlCmdVariablesForDeploy();
|
||||||
const profile: IDeploymentProfile = {
|
const profile: IDeploymentProfile = {
|
||||||
databaseName: this.getTargetDatabaseName(),
|
databaseName: this.getTargetDatabaseName(),
|
||||||
upgradeExisting: true,
|
upgradeExisting: true,
|
||||||
connectionUri: await this.getConnectionUri(),
|
connectionUri: await this.getConnectionUri(),
|
||||||
sqlCmdVariables: this.project.sqlCmdVariables
|
sqlCmdVariables: sqlCmdVars
|
||||||
};
|
};
|
||||||
|
|
||||||
this.apiWrapper.closeDialog(this.dialog);
|
this.apiWrapper.closeDialog(this.dialog);
|
||||||
@@ -174,10 +207,11 @@ export class DeployDatabaseDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async generateScriptClick(): Promise<void> {
|
public async generateScriptClick(): Promise<void> {
|
||||||
|
const sqlCmdVars = this.getSqlCmdVariablesForDeploy();
|
||||||
const profile: IGenerateScriptProfile = {
|
const profile: IGenerateScriptProfile = {
|
||||||
databaseName: this.getTargetDatabaseName(),
|
databaseName: this.getTargetDatabaseName(),
|
||||||
connectionUri: await this.getConnectionUri(),
|
connectionUri: await this.getConnectionUri(),
|
||||||
sqlCmdVariables: this.project.sqlCmdVariables
|
sqlCmdVariables: sqlCmdVars
|
||||||
};
|
};
|
||||||
|
|
||||||
this.apiWrapper.closeDialog(this.dialog);
|
this.apiWrapper.closeDialog(this.dialog);
|
||||||
@@ -189,6 +223,18 @@ export class DeployDatabaseDialog {
|
|||||||
this.dispose();
|
this.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSqlCmdVariablesForDeploy(): Record<string, string> {
|
||||||
|
// get SQLCMD variables from project
|
||||||
|
let sqlCmdVariables = { ...this.project.sqlCmdVariables };
|
||||||
|
|
||||||
|
// update with SQLCMD variables loaded from profile if there are any
|
||||||
|
for (const key in this.profileSqlCmdVars) {
|
||||||
|
sqlCmdVariables[key] = this.profileSqlCmdVars[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqlCmdVariables;
|
||||||
|
}
|
||||||
|
|
||||||
public getTargetDatabaseName(): string {
|
public getTargetDatabaseName(): string {
|
||||||
return this.targetDatabaseTextBox?.value ?? '';
|
return this.targetDatabaseTextBox?.value ?? '';
|
||||||
}
|
}
|
||||||
@@ -344,6 +390,64 @@ export class DeployDatabaseDialog {
|
|||||||
return clearButton;
|
return clearButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createLoadProfileButton(view: azdata.ModelView): azdata.ButtonComponent {
|
||||||
|
let loadProfileButton: azdata.ButtonComponent = view.modelBuilder.button().withProperties({
|
||||||
|
label: constants.loadProfileButtonText,
|
||||||
|
title: constants.loadProfileButtonText,
|
||||||
|
ariaLabel: constants.loadProfileButtonText,
|
||||||
|
width: '120px'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
loadProfileButton.onDidClick(async () => {
|
||||||
|
const fileUris = await this.apiWrapper.showOpenDialog(
|
||||||
|
{
|
||||||
|
canSelectFiles: true,
|
||||||
|
canSelectFolders: false,
|
||||||
|
canSelectMany: false,
|
||||||
|
defaultUri: vscode.Uri.parse(this.project.projectFolderPath),
|
||||||
|
filters: {
|
||||||
|
[constants.publishSettingsFiles]: ['publish.xml']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fileUris || fileUris.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.readPublishProfile) {
|
||||||
|
const result = await this.readPublishProfile(fileUris[0]);
|
||||||
|
(<azdata.InputBoxComponent>this.targetDatabaseTextBox).value = result.databaseName;
|
||||||
|
this.profileSqlCmdVars = result.sqlCmdVariables;
|
||||||
|
const data = this.convertSqlCmdVarsToTableFormat(this.getSqlCmdVariablesForDeploy());
|
||||||
|
|
||||||
|
(<azdata.TableComponent>this.sqlCmdVariablesTable).updateProperties({
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
|
||||||
|
// add SQLCMD Variables table if it wasn't there before
|
||||||
|
if (Object.keys(this.project.sqlCmdVariables).length === 0) {
|
||||||
|
this.formBuilder?.insertFormItem({
|
||||||
|
title: constants.sqlCmdTableLabel,
|
||||||
|
component: <azdata.TableComponent>this.sqlCmdVariablesTable
|
||||||
|
},
|
||||||
|
6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return loadProfileButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
private convertSqlCmdVarsToTableFormat(sqlCmdVars: Record<string, string>): string[][] {
|
||||||
|
let data = [];
|
||||||
|
for (let key in sqlCmdVars) {
|
||||||
|
data.push([key, sqlCmdVars[key]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
// only enable Generate Script and Ok buttons if all fields are filled
|
// only enable Generate Script and Ok buttons if all fields are filled
|
||||||
private tryEnableGenerateScriptAndOkButtons(): void {
|
private tryEnableGenerateScriptAndOkButtons(): void {
|
||||||
if (this.targetConnectionTextBox!.value && this.targetDatabaseTextBox!.value
|
if (this.targetConnectionTextBox!.value && this.targetDatabaseTextBox!.value
|
||||||
|
|||||||
@@ -15,3 +15,8 @@ export interface IGenerateScriptProfile {
|
|||||||
connectionUri: string;
|
connectionUri: string;
|
||||||
sqlCmdVariables?: Record<string, string>;
|
sqlCmdVariables?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PublishSettings {
|
||||||
|
databaseName: string;
|
||||||
|
sqlCmdVariables: Record<string, string>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,13 +64,7 @@ export class Project {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find all SQLCMD variables to include
|
// find all SQLCMD variables to include
|
||||||
for (let i = 0; i < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable).length; i++) {
|
this.sqlCmdVariables = utils.readSqlCmdVariables(this.projFileXmlDoc);
|
||||||
const sqlCmdVar = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable)[i];
|
|
||||||
const varName = sqlCmdVar.getAttribute(constants.Include);
|
|
||||||
|
|
||||||
const varValue = sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0].childNodes[0].nodeValue;
|
|
||||||
this.sqlCmdVariables[varName] = varValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find all database references to include
|
// find all database references to include
|
||||||
for (let r = 0; r < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference).length; r++) {
|
for (let r = 0; r < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference).length; r++) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export let SSDTUpdatedProjectAfterSystemDbUpdateBaselineWindows: string;
|
|||||||
export let SSDTUpdatedProjectAfterSystemDbUpdateBaseline: string;
|
export let SSDTUpdatedProjectAfterSystemDbUpdateBaseline: string;
|
||||||
export let SSDTProjectBaselineWithCleanTarget: string;
|
export let SSDTProjectBaselineWithCleanTarget: string;
|
||||||
export let SSDTProjectBaselineWithCleanTargetAfterUpdate: string;
|
export let SSDTProjectBaselineWithCleanTargetAfterUpdate: string;
|
||||||
|
export let publishProfileBaseline: string;
|
||||||
|
|
||||||
const baselineFolderPath = __dirname;
|
const baselineFolderPath = __dirname;
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ export async function loadBaselines() {
|
|||||||
SSDTUpdatedProjectAfterSystemDbUpdateBaseline = await loadBaseline(baselineFolderPath, 'SSDTUpdatedProjectAfterSystemDbUpdateBaseline.xml');
|
SSDTUpdatedProjectAfterSystemDbUpdateBaseline = await loadBaseline(baselineFolderPath, 'SSDTUpdatedProjectAfterSystemDbUpdateBaseline.xml');
|
||||||
SSDTProjectBaselineWithCleanTarget = await loadBaseline(baselineFolderPath, 'SSDTProjectBaselineWithCleanTarget.xml');
|
SSDTProjectBaselineWithCleanTarget = await loadBaseline(baselineFolderPath, 'SSDTProjectBaselineWithCleanTarget.xml');
|
||||||
SSDTProjectBaselineWithCleanTargetAfterUpdate = await loadBaseline(baselineFolderPath, 'SSDTProjectBaselineWithCleanTargetAfterUpdate.xml');
|
SSDTProjectBaselineWithCleanTargetAfterUpdate = await loadBaseline(baselineFolderPath, 'SSDTProjectBaselineWithCleanTargetAfterUpdate.xml');
|
||||||
|
publishProfileBaseline = await loadBaseline(baselineFolderPath, 'publishProfileBaseline.publish.xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadBaseline(baselineFolderPath: string, fileName: string): Promise<string> {
|
async function loadBaseline(baselineFolderPath: string, fileName: string): Promise<string> {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<IncludeCompositeObjects>True</IncludeCompositeObjects>
|
||||||
|
<TargetDatabaseName>targetDb</TargetDatabaseName>
|
||||||
|
<DeployScriptFileName>DatabaseProject1.sql</DeployScriptFileName>
|
||||||
|
<ProfileVersionNumber>1</ProfileVersionNumber>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<SqlCmdVariable Include="ProdDatabaseName">
|
||||||
|
<DefaultValue>MyProdDatabase</DefaultValue>
|
||||||
|
<Value>$(SqlCmdVar__1)</Value>
|
||||||
|
</SqlCmdVariable>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -117,6 +117,7 @@ describe.skip('ProjectsController: project controller operations', function ():
|
|||||||
|
|
||||||
const deployHoller = 'hello from callback for deploy()';
|
const deployHoller = 'hello from callback for deploy()';
|
||||||
const generateHoller = 'hello from callback for generateScript()';
|
const generateHoller = 'hello from callback for generateScript()';
|
||||||
|
const profileHoller = 'hello from callback for readPublishProfile()';
|
||||||
|
|
||||||
let holler = 'nothing';
|
let holler = 'nothing';
|
||||||
|
|
||||||
@@ -131,6 +132,13 @@ describe.skip('ProjectsController: project controller operations', function ():
|
|||||||
holler = deployHoller;
|
holler = deployHoller;
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
projController.setup(x => x.readPublishProfile(TypeMoq.It.isAny())).returns(async () => {
|
||||||
|
holler = profileHoller;
|
||||||
|
return {
|
||||||
|
databaseName: '',
|
||||||
|
sqlCmdVariables: {}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
projController.setup(x => x.executionCallback(TypeMoq.It.isAny(), TypeMoq.It.is((_): _ is IGenerateScriptProfile => true))).returns(async () => {
|
projController.setup(x => x.executionCallback(TypeMoq.It.isAny(), TypeMoq.It.is((_): _ is IGenerateScriptProfile => true))).returns(async () => {
|
||||||
holler = generateHoller;
|
holler = generateHoller;
|
||||||
@@ -146,6 +154,22 @@ describe.skip('ProjectsController: project controller operations', function ():
|
|||||||
await dialog.generateScriptClick();
|
await dialog.generateScriptClick();
|
||||||
|
|
||||||
should(holler).equal(generateHoller, 'executionCallback() is supposed to have been setup and called for GenerateScript scenario');
|
should(holler).equal(generateHoller, 'executionCallback() is supposed to have been setup and called for GenerateScript scenario');
|
||||||
|
|
||||||
|
dialog = await projController.object.deployProject(proj);
|
||||||
|
await projController.object.readPublishProfile(vscode.Uri.parse('test'));
|
||||||
|
|
||||||
|
should(holler).equal(profileHoller, 'executionCallback() is supposed to have been setup and called for ReadPublishProfile scenario');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should read database name and SQLCMD variables from publish profile', async function (): Promise<void> {
|
||||||
|
await baselines.loadBaselines();
|
||||||
|
let profilePath = await testUtils.createTestFile(baselines.publishProfileBaseline, 'publishProfile.publish.xml');
|
||||||
|
const projController = new ProjectsController(testContext.apiWrapper.object, new SqlDatabaseProjectTreeViewProvider());
|
||||||
|
|
||||||
|
let result = await projController.readPublishProfile(vscode.Uri.parse(profilePath));
|
||||||
|
should(result.databaseName).equal('targetDb');
|
||||||
|
should(Object.keys(result.sqlCmdVariables).length).equal(1);
|
||||||
|
should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export async function generateTestFolderPath(): Promise<string> {
|
|||||||
return folderPath;
|
return folderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createTestFile(contents: string, fileName: string, folderPath?: string): Promise<string> {
|
export async function createTestFile(contents: string, fileName: string, folderPath?: string): Promise<string> {
|
||||||
folderPath = folderPath ?? await generateTestFolderPath();
|
folderPath = folderPath ?? await generateTestFolderPath();
|
||||||
const filePath = path.join(folderPath, fileName);
|
const filePath = path.join(folderPath, fileName);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user