Hook up add database references dialog (#12002)

* initial dialog

* got enabling working

* add tests

* cleanup

* add test coverage for systemDbRadioButtonClick()

* change DAC to .dacpac

* remove isEmptyOrUndefined

* hook up add database reference dialog

* cleanup

* Addressing comments
This commit is contained in:
Kim Santiago
2020-09-01 11:06:04 -07:00
committed by GitHub
parent 8f8d01cee2
commit 177d9bef39
5 changed files with 195 additions and 266 deletions

View File

@@ -7,7 +7,7 @@ import { DatabaseReferenceLocation, SystemDatabase } from './project';
import { Uri } from 'vscode';
export interface IDatabaseReferenceSettings {
databaseName: string;
databaseName?: string;
}
export interface ISystemDatabaseReferenceSettings extends IDatabaseReferenceSettings {

View File

@@ -14,6 +14,7 @@ import * as templates from '../templates/templates';
import { Uri, window } from 'vscode';
import { promises as fs } from 'fs';
import { DataSource } from './dataSources/dataSources';
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings } from './IDatabaseReferenceSettings';
/**
* Class representing a Project, and providing functions for operating on it
@@ -123,7 +124,12 @@ export class Project {
const nameNodes = references[r].getElementsByTagName(constants.DatabaseVariableLiteralValue);
const name = nameNodes.length === 1 ? nameNodes[0].childNodes[0].nodeValue : undefined;
this.databaseReferences.push(new DacpacReferenceProjectEntry(Uri.file(filepath), name ? DatabaseReferenceLocation.differentDatabaseSameServer : DatabaseReferenceLocation.sameDatabase, name));
this.databaseReferences.push(new DacpacReferenceProjectEntry({
dacpacFileLocation: Uri.file(utils.getPlatformSafeFileEntryPath(filepath)),
databaseLocation: name ? DatabaseReferenceLocation.differentDatabaseSameServer : DatabaseReferenceLocation.sameDatabase,
databaseName: name
}));
}
}
@@ -285,21 +291,19 @@ export class Project {
/**
* Adds reference to the appropriate system database dacpac to the project
*/
public async addSystemDatabaseReference(name: SystemDatabase): Promise<void> {
public async addSystemDatabaseReference(settings: ISystemDatabaseReferenceSettings): Promise<void> {
let uri: Uri;
let ssdtUri: Uri;
let dbName: string;
if (name === SystemDatabase.master) {
if (settings.systemDb === SystemDatabase.master) {
uri = this.getSystemDacpacUri(constants.masterDacpac);
ssdtUri = this.getSystemDacpacSsdtUri(constants.masterDacpac);
dbName = constants.master;
} else {
uri = this.getSystemDacpacUri(constants.msdbDacpac);
ssdtUri = this.getSystemDacpacSsdtUri(constants.msdbDacpac);
dbName = constants.msdb;
}
let systemDatabaseReferenceProjectEntry = new SystemDatabaseReferenceProjectEntry(uri, ssdtUri, dbName);
const systemDatabaseReferenceProjectEntry = new SystemDatabaseReferenceProjectEntry(uri, ssdtUri, <string>settings.databaseName);
await this.addToProjFile(systemDatabaseReferenceProjectEntry);
}
@@ -340,8 +344,8 @@ export class Project {
* @param uri Uri of the dacpac
* @param databaseName name of the database
*/
public async addDatabaseReference(uri: Uri, databaseLocation: DatabaseReferenceLocation, databaseName?: string): Promise<void> {
let databaseReferenceEntry = new DacpacReferenceProjectEntry(uri, databaseLocation, databaseName);
public async addDatabaseReference(settings: IDacpacReferenceSettings): Promise<void> {
const databaseReferenceEntry = new DacpacReferenceProjectEntry(settings);
await this.addToProjFile(databaseReferenceEntry);
}
@@ -432,32 +436,41 @@ export class Project {
throw new Error(constants.unableToFindObject(path, constants.folderObject));
}
private addSystemDatabaseReferenceToProjFile(entry: SystemDatabaseReferenceProjectEntry): void {
const systemDbReferenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference);
// if it's a system database reference, we'll add an additional node with the SSDT location of the dacpac later
systemDbReferenceNode.setAttribute(constants.Condition, constants.NetCoreCondition);
systemDbReferenceNode.setAttribute(constants.Include, entry.pathForSqlProj());
this.addDatabaseReferenceChildren(systemDbReferenceNode, entry);
this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(systemDbReferenceNode);
this.databaseReferences.push(entry);
// add a reference to the system dacpac in SSDT if it's a system db
const ssdtReferenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference);
ssdtReferenceNode.setAttribute(constants.Condition, constants.NotNetCoreCondition);
ssdtReferenceNode.setAttribute(constants.Include, entry.ssdtPathForSqlProj());
this.addDatabaseReferenceChildren(ssdtReferenceNode, entry);
this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(ssdtReferenceNode);
}
private addDatabaseReferenceToProjFile(entry: IDatabaseReferenceProjectEntry): void {
// check if reference to this database already exists
if (this.databaseReferenceExists(entry)) {
throw new Error(constants.databaseReferenceAlreadyExists);
}
let referenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference);
const isSystemDatabaseProjectEntry = (<SystemDatabaseReferenceProjectEntry>entry).ssdtUri;
// if it's a system database reference, we'll add an additional node with the SSDT location of the dacpac later
if (isSystemDatabaseProjectEntry) {
referenceNode.setAttribute(constants.Condition, constants.NetCoreCondition);
}
this.addSystemDatabaseReferenceToProjFile(<SystemDatabaseReferenceProjectEntry>entry);
} else {
referenceNode.setAttribute(constants.Include, entry.pathForSqlProj());
this.addDatabaseReferenceChildren(referenceNode, entry.sqlCmdName);
this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(referenceNode);
this.databaseReferences.push(entry);
// add a reference to the system dacpac in SSDT if it's a system db
if (isSystemDatabaseProjectEntry) {
let ssdtReferenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference);
ssdtReferenceNode.setAttribute(constants.Condition, constants.NotNetCoreCondition);
ssdtReferenceNode.setAttribute(constants.Include, (<SystemDatabaseReferenceProjectEntry>entry).ssdtPathForSqlProj());
this.addDatabaseReferenceChildren(ssdtReferenceNode, entry.sqlCmdName);
this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(ssdtReferenceNode);
const referenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference);
referenceNode.setAttribute(constants.Include, entry.pathForSqlProj());
this.addDatabaseReferenceChildren(referenceNode, entry);
this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(referenceNode);
this.databaseReferences.push(entry);
}
}
@@ -466,17 +479,19 @@ export class Project {
return found;
}
private addDatabaseReferenceChildren(referenceNode: any, name?: string): void {
let suppressMissingDependenciesErrorNode = this.projFileXmlDoc.createElement(constants.SuppressMissingDependenciesErrors);
let falseTextNode = this.projFileXmlDoc.createTextNode('False');
private addDatabaseReferenceChildren(referenceNode: any, entry: IDatabaseReferenceProjectEntry): void {
// TODO: create checkbox for this setting
const suppressMissingDependenciesErrorNode = this.projFileXmlDoc.createElement(constants.SuppressMissingDependenciesErrors);
const falseTextNode = this.projFileXmlDoc.createTextNode('False');
suppressMissingDependenciesErrorNode.appendChild(falseTextNode);
referenceNode.appendChild(suppressMissingDependenciesErrorNode);
if (name) {
let databaseVariableLiteralValue = this.projFileXmlDoc.createElement(constants.DatabaseVariableLiteralValue);
let databaseTextNode = this.projFileXmlDoc.createTextNode(name);
databaseVariableLiteralValue.appendChild(databaseTextNode);
referenceNode.appendChild(databaseVariableLiteralValue);
// TODO: add support for sqlcmd vars and server https://github.com/microsoft/azuredatastudio/issues/12036
if (entry.databaseVariableLiteralValue) {
const databaseVariableLiteralValueElement = this.projFileXmlDoc.createElement(constants.DatabaseVariableLiteralValue);
const databaseTextNode = this.projFileXmlDoc.createTextNode(entry.databaseVariableLiteralValue);
databaseVariableLiteralValueElement.appendChild(databaseTextNode);
referenceNode.appendChild(databaseVariableLiteralValueElement);
}
}
@@ -521,13 +536,22 @@ export class Project {
return false;
}
/**
* Update system db references to have the ADS and SSDT paths to the system dacpacs
*/
public async updateSystemDatabaseReferencesInProjFile(): Promise<void> {
// find all system database references
for (let r = 0; r < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference).length; r++) {
const currentNode = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ArtifactReference)[r];
if (!currentNode.getAttribute(constants.Condition) && currentNode.getAttribute(constants.Include).includes(constants.DacpacRootPath)) {
// get name of system database
const name = currentNode.getAttribute(constants.Include).includes(constants.master) ? SystemDatabase.master : SystemDatabase.msdb;
const systemDb = currentNode.getAttribute(constants.Include).includes(constants.master) ? SystemDatabase.master : SystemDatabase.msdb;
// get name
const nameNodes = currentNode.getElementsByTagName(constants.DatabaseVariableLiteralValue);
const databaseVariableName = nameNodes[0].childNodes[0]?.nodeValue;
// remove this node
this.projFileXmlDoc.documentElement.removeChild(currentNode);
// delete ItemGroup if there aren't any other children
@@ -536,9 +560,9 @@ export class Project {
}
// remove from database references because it'll get added again later
this.databaseReferences.splice(this.databaseReferences.findIndex(n => n.databaseName === (name === SystemDatabase.master ? constants.master : constants.msdb)), 1);
this.databaseReferences.splice(this.databaseReferences.findIndex(n => n.databaseName === (systemDb === SystemDatabase.master ? constants.master : constants.msdb)), 1);
await this.addSystemDatabaseReference(name);
await this.addSystemDatabaseReference({ databaseName: databaseVariableName, systemDb: systemDb });
}
}
}
@@ -643,26 +667,37 @@ export class ProjectEntry {
export interface IDatabaseReferenceProjectEntry extends ProjectEntry {
databaseName: string;
sqlCmdName?: string | undefined;
databaseVariableLiteralValue?: string;
}
export class DacpacReferenceProjectEntry extends ProjectEntry implements IDatabaseReferenceProjectEntry {
sqlCmdName: string | undefined;
databaseLocation: DatabaseReferenceLocation;
databaseVariableLiteralValue?: string;
constructor(uri: Uri, public databaseLocation: DatabaseReferenceLocation, name?: string) {
super(uri, '', EntryType.DatabaseReference);
this.sqlCmdName = name;
constructor(settings: IDacpacReferenceSettings) {
super(settings.dacpacFileLocation, '', EntryType.DatabaseReference);
this.databaseLocation = settings.databaseLocation;
this.databaseVariableLiteralValue = settings.databaseName;
}
/**
* File name that gets displayed in the project tree
*/
public get databaseName(): string {
return path.parse(utils.getPlatformSafeFileEntryPath(this.fsUri.fsPath)).name;
}
}
class SystemDatabaseReferenceProjectEntry extends DacpacReferenceProjectEntry {
constructor(uri: Uri, public ssdtUri: Uri, name: string) {
super(uri, DatabaseReferenceLocation.differentDatabaseSameServer, name);
this.sqlCmdName = name;
class SystemDatabaseReferenceProjectEntry extends ProjectEntry implements IDatabaseReferenceProjectEntry {
constructor(uri: Uri, public ssdtUri: Uri, public databaseVariableLiteralValue: string) {
super(uri, '', EntryType.DatabaseReference);
}
/**
* File name that gets displayed in the project tree
*/
public get databaseName(): string {
return path.parse(utils.getPlatformSafeFileEntryPath(this.fsUri.fsPath)).name;
}
public pathForSqlProj(): string {