mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 09:35:39 -05:00
Add reference to another sql project (#12186)
* add projects to add database reference dialog * able to add project references * check for circular dependency * only allow adding reference to project in the same workspace * fix location dropdown when project reference is enabled * add tests * more tests * cleanup * fix flakey test * addressing comments
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DatabaseReferenceLocation, SystemDatabase } from './project';
|
||||
import { SystemDatabase } from './project';
|
||||
import { Uri } from 'vscode';
|
||||
|
||||
export interface IDatabaseReferenceSettings {
|
||||
@@ -16,9 +16,17 @@ export interface ISystemDatabaseReferenceSettings extends IDatabaseReferenceSett
|
||||
}
|
||||
|
||||
export interface IDacpacReferenceSettings extends IDatabaseReferenceSettings {
|
||||
databaseLocation: DatabaseReferenceLocation;
|
||||
dacpacFileLocation: Uri;
|
||||
databaseVariable?: string;
|
||||
serverName?: string;
|
||||
serverVariable?: string;
|
||||
}
|
||||
|
||||
export interface IProjectReferenceSettings extends IDatabaseReferenceSettings {
|
||||
projectRelativePath: Uri | undefined;
|
||||
projectName: string;
|
||||
projectGuid: string;
|
||||
databaseVariable?: string;
|
||||
serverName?: string;
|
||||
serverVariable?: string;
|
||||
}
|
||||
|
||||
@@ -14,7 +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';
|
||||
import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings, IProjectReferenceSettings } from './IDatabaseReferenceSettings';
|
||||
|
||||
/**
|
||||
* Class representing a Project, and providing functions for operating on it
|
||||
@@ -22,6 +22,7 @@ import { ISystemDatabaseReferenceSettings, IDacpacReferenceSettings } from './ID
|
||||
export class Project {
|
||||
public projectFilePath: string;
|
||||
public projectFileName: string;
|
||||
public projectGuid: string | undefined;
|
||||
public files: FileProjectEntry[] = [];
|
||||
public dataSources: DataSource[] = [];
|
||||
public importedTargets: string[] = [];
|
||||
@@ -61,6 +62,9 @@ export class Project {
|
||||
const projFileText = await fs.readFile(this.projectFilePath);
|
||||
this.projFileXmlDoc = new xmldom.DOMParser().parseFromString(projFileText.toString());
|
||||
|
||||
// get projectGUID
|
||||
this.projectGuid = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ProjectGuid)[0].childNodes[0].nodeValue;
|
||||
|
||||
// find all folders and files to include
|
||||
for (let ig = 0; ig < this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ItemGroup).length; ig++) {
|
||||
const itemGroup = this.projFileXmlDoc.documentElement.getElementsByTagName(constants.ItemGroup)[ig];
|
||||
@@ -128,13 +132,12 @@ export class Project {
|
||||
const name = nameNodes.length === 1 ? nameNodes[0].childNodes[0].nodeValue : undefined;
|
||||
|
||||
const suppressMissingDependenciesErrorNode = references[r].getElementsByTagName(constants.SuppressMissingDependenciesErrors);
|
||||
const suppressMissingDependences = suppressMissingDependenciesErrorNode[0].childNodes[0].nodeValue === true ?? false;
|
||||
const suppressMissingDependencies = suppressMissingDependenciesErrorNode[0].childNodes[0].nodeValue === true ?? false;
|
||||
|
||||
this.databaseReferences.push(new DacpacReferenceProjectEntry({
|
||||
dacpacFileLocation: Uri.file(utils.getPlatformSafeFileEntryPath(filepath)),
|
||||
databaseLocation: name ? DatabaseReferenceLocation.differentDatabaseSameServer : DatabaseReferenceLocation.sameDatabase,
|
||||
databaseName: name,
|
||||
suppressMissingDependenciesErrors: suppressMissingDependences
|
||||
suppressMissingDependenciesErrors: suppressMissingDependencies
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -151,9 +154,14 @@ export class Project {
|
||||
const name = nameNodes[0].childNodes[0].nodeValue;
|
||||
|
||||
const suppressMissingDependenciesErrorNode = projectReferences[r].getElementsByTagName(constants.SuppressMissingDependenciesErrors);
|
||||
const suppressMissingDependences = suppressMissingDependenciesErrorNode[0].childNodes[0].nodeValue === true ?? false;
|
||||
const suppressMissingDependencies = suppressMissingDependenciesErrorNode[0].childNodes[0].nodeValue === true ?? false;
|
||||
|
||||
this.databaseReferences.push(new SqlProjectReferenceProjectEntry(Uri.file(utils.getPlatformSafeFileEntryPath(filepath)), name, suppressMissingDependences));
|
||||
this.databaseReferences.push(new SqlProjectReferenceProjectEntry({
|
||||
projectRelativePath: Uri.file(utils.getPlatformSafeFileEntryPath(filepath)),
|
||||
projectName: name,
|
||||
projectGuid: '', // don't care when just reading project as a reference
|
||||
suppressMissingDependenciesErrors: suppressMissingDependencies
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,6 +382,16 @@ export class Project {
|
||||
await this.addToProjFile(databaseReferenceEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds reference to a another project in the workspace
|
||||
* @param uri Uri of the dacpac
|
||||
* @param databaseName name of the database
|
||||
*/
|
||||
public async addProjectReference(settings: IProjectReferenceSettings): Promise<void> {
|
||||
const projectReferenceEntry = new SqlProjectReferenceProjectEntry(settings);
|
||||
await this.addToProjFile(projectReferenceEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a SQLCMD variable to the project
|
||||
* @param name name of the variable
|
||||
@@ -529,10 +547,15 @@ export class Project {
|
||||
throw new Error(constants.databaseReferenceAlreadyExists);
|
||||
}
|
||||
|
||||
const isSystemDatabaseProjectEntry = (<SystemDatabaseReferenceProjectEntry>entry).ssdtUri;
|
||||
|
||||
if (isSystemDatabaseProjectEntry) {
|
||||
if (entry instanceof SystemDatabaseReferenceProjectEntry) {
|
||||
this.addSystemDatabaseReferenceToProjFile(<SystemDatabaseReferenceProjectEntry>entry);
|
||||
} else if (entry instanceof SqlProjectReferenceProjectEntry) {
|
||||
const referenceNode = this.projFileXmlDoc.createElement(constants.ProjectReference);
|
||||
referenceNode.setAttribute(constants.Include, entry.pathForSqlProj());
|
||||
this.addProjectReferenceChildren(referenceNode, <SqlProjectReferenceProjectEntry>entry);
|
||||
this.addDatabaseReferenceChildren(referenceNode, entry);
|
||||
this.findOrCreateItemGroup(constants.ProjectReference).appendChild(referenceNode);
|
||||
this.databaseReferences.push(entry);
|
||||
} else {
|
||||
const referenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference);
|
||||
referenceNode.setAttribute(constants.Include, entry.pathForSqlProj());
|
||||
@@ -579,6 +602,26 @@ export class Project {
|
||||
}
|
||||
}
|
||||
|
||||
private addProjectReferenceChildren(referenceNode: any, entry: SqlProjectReferenceProjectEntry): void {
|
||||
// project name
|
||||
const nameElement = this.projFileXmlDoc.createElement(constants.Name);
|
||||
const nameTextNode = this.projFileXmlDoc.createTextNode(entry.projectName);
|
||||
nameElement.appendChild(nameTextNode);
|
||||
referenceNode.appendChild(nameElement);
|
||||
|
||||
// add project guid
|
||||
const projectElement = this.projFileXmlDoc.createElement(constants.Project);
|
||||
const projectGuidTextNode = this.projFileXmlDoc.createTextNode(entry.projectGuid);
|
||||
projectElement.appendChild(projectGuidTextNode);
|
||||
referenceNode.appendChild(projectElement);
|
||||
|
||||
// add Private (not sure what this is for)
|
||||
const privateElement = this.projFileXmlDoc.createElement(constants.Private);
|
||||
const privateTextNode = this.projFileXmlDoc.createTextNode(constants.True);
|
||||
privateElement.appendChild(privateTextNode);
|
||||
referenceNode.appendChild(privateElement);
|
||||
}
|
||||
|
||||
public addSqlCmdVariableToProjFile(entry: SqlCmdVariableProjectEntry): void {
|
||||
// Remove any entries with the same variable name. It'll be replaced with a new one
|
||||
this.removeFromProjFile(entry);
|
||||
@@ -821,7 +864,6 @@ export interface IDatabaseReferenceProjectEntry extends FileProjectEntry {
|
||||
}
|
||||
|
||||
export class DacpacReferenceProjectEntry extends FileProjectEntry implements IDatabaseReferenceProjectEntry {
|
||||
databaseLocation: DatabaseReferenceLocation;
|
||||
databaseVariableLiteralValue?: string;
|
||||
databaseSqlCmdVariable?: string;
|
||||
serverName?: string;
|
||||
@@ -830,7 +872,6 @@ export class DacpacReferenceProjectEntry extends FileProjectEntry implements IDa
|
||||
|
||||
constructor(settings: IDacpacReferenceSettings) {
|
||||
super(settings.dacpacFileLocation, '', EntryType.DatabaseReference);
|
||||
this.databaseLocation = settings.databaseLocation;
|
||||
this.databaseSqlCmdVariable = settings.databaseVariable;
|
||||
this.databaseVariableLiteralValue = settings.databaseName;
|
||||
this.serverName = settings.serverName;
|
||||
@@ -870,13 +911,33 @@ class SystemDatabaseReferenceProjectEntry extends FileProjectEntry implements ID
|
||||
}
|
||||
|
||||
export class SqlProjectReferenceProjectEntry extends FileProjectEntry implements IDatabaseReferenceProjectEntry {
|
||||
constructor(uri: Uri, public projectName: string, public suppressMissingDependenciesErrors: boolean) {
|
||||
super(uri, '', EntryType.DatabaseReference);
|
||||
projectName: string;
|
||||
projectGuid: string;
|
||||
databaseVariableLiteralValue?: string;
|
||||
databaseSqlCmdVariable?: string;
|
||||
serverName?: string;
|
||||
serverSqlCmdVariable?: string;
|
||||
suppressMissingDependenciesErrors: boolean;
|
||||
|
||||
constructor(settings: IProjectReferenceSettings) {
|
||||
super(settings.projectRelativePath!, '', EntryType.DatabaseReference);
|
||||
this.projectName = settings.projectName;
|
||||
this.projectGuid = settings.projectGuid;
|
||||
this.databaseSqlCmdVariable = settings.databaseVariable;
|
||||
this.databaseVariableLiteralValue = settings.databaseName;
|
||||
this.serverName = settings.serverName;
|
||||
this.serverSqlCmdVariable = settings.serverVariable;
|
||||
this.suppressMissingDependenciesErrors = settings.suppressMissingDependenciesErrors;
|
||||
}
|
||||
|
||||
public get databaseName(): string {
|
||||
return this.projectName;
|
||||
}
|
||||
|
||||
public pathForSqlProj(): string {
|
||||
// need to remove the leading slash from path for build to work on Windows
|
||||
return utils.convertSlashesForSqlProj(this.fsUri.path.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
export class SqlCmdVariableProjectEntry extends ProjectEntry {
|
||||
|
||||
Reference in New Issue
Block a user