mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Add option for msdb reference in sql project (#10810)
* add msdb option * add msdb dacpacs * add tests * fix system dacpac path for windows
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -16,6 +16,9 @@ export const sqlDatabaseProjectExtensionId = 'microsoft.sql-database-projects';
|
||||
export const mssqlExtensionId = 'microsoft.mssql';
|
||||
export const dacpac = 'dacpac';
|
||||
export const master = 'master';
|
||||
export const masterDacpac = 'master.dacpac';
|
||||
export const msdb = 'msdb';
|
||||
export const msdbDacpac = 'msdb.dacpac';
|
||||
export const MicrosoftDatatoolsSchemaSqlSql = 'Microsoft.Data.Tools.Schema.Sql.Sql';
|
||||
export const databaseSchemaProvider = 'DatabaseSchemaProvider';
|
||||
|
||||
@@ -32,11 +35,13 @@ export const noString = localize('noString', "No");
|
||||
export const extractTargetInput = localize('extractTargetInput', "Target for extraction:");
|
||||
export const selectString = localize('selectString', "Select");
|
||||
export const addDatabaseReferenceInput = localize('addDatabaseReferenceInput', "Add database reference for:");
|
||||
export const systemDatabaseReferenceInput = localize('systemDatabaseReferenceInput', "System Database:");
|
||||
export const databaseReferenceLocation = localize('databaseReferenceLocation', "Database location");
|
||||
export const databaseReferenceSameDatabase = localize('databaseReferenceSameDatabase', "Same database");
|
||||
export const databaseReferenceDifferentDabaseSameServer = localize('databaseReferenceDifferentDabaseSameServer', "Different database, same server");
|
||||
export const databaseReferenceDatabaseName = localize('databaseReferenceDatabaseName', "Database name");
|
||||
export const dacpacFiles = localize('dacpacFiles', "dacpac Files");
|
||||
export const systemDatabase = localize('systemDatabase', "System Database");
|
||||
export function newObjectNamePrompt(objectType: string) { return localize('newObjectNamePrompt', 'New {0} name:', objectType); }
|
||||
|
||||
// Deploy dialog strings
|
||||
@@ -74,6 +79,7 @@ export const schemaCompareNotInstalled = localize('schemaCompareNotInstalled', "
|
||||
export const buildDacpacNotFound = localize('buildDacpacNotFound', "Dacpac created from build not found");
|
||||
export const updateProjectForRoundTrip = localize('updateProjectForRoundTrip', "To build this project, Azure Data Studio needs to update targets and references. If the project is created in SSDT, it will continue to work in both tools. Do you want Azure Data Studio to update the project?");
|
||||
export const databaseReferenceTypeRequired = localize('databaseReferenceTypeRequired', "Database reference type is required for adding a reference to a database");
|
||||
export const systemDatabaseReferenceRequired = localize('systemDatabaseReferenceRequired', "System database selection is required for adding a reference to a system database");
|
||||
export const dacpacFileLocationRequired = localize('dacpacFileLocationRequired', "Dacpac file location is required for adding a reference to a database");
|
||||
export const databaseLocationRequired = localize('databaseLocation', "Database location is required for adding a reference to a database");
|
||||
export const databaseNameRequired = localize('databaseNameRequired', "Database name is required for adding a reference to a different database");
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IConnectionProfile, TaskExecutionMode } from 'azdata';
|
||||
import { promises as fs } from 'fs';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
import { DeployDatabaseDialog } from '../dialogs/deployDatabaseDialog';
|
||||
import { Project, DatabaseReferenceLocation } from '../models/project';
|
||||
import { Project, DatabaseReferenceLocation, SystemDatabase, TargetPlatform } from '../models/project';
|
||||
import { SqlDatabaseProjectTreeViewProvider } from './databaseProjectTreeViewProvider';
|
||||
import { FolderNode } from '../models/tree/fileFolderTreeItem';
|
||||
import { IDeploymentProfile, IGenerateScriptProfile } from '../models/IDeploymentProfile';
|
||||
@@ -306,8 +306,9 @@ export class ProjectsController {
|
||||
const databaseReferenceType = await this.getDatabaseReferenceType();
|
||||
|
||||
// if master is selected, we know which dacpac needs to be added
|
||||
if (databaseReferenceType === constants.master) {
|
||||
await project.addMasterDatabaseReference();
|
||||
if (databaseReferenceType === constants.systemDatabase) {
|
||||
const systemDatabase = await this.getSystemDatabaseName(project);
|
||||
await project.addSystemDatabaseReference(systemDatabase);
|
||||
} else {
|
||||
// get other information needed to add a reference to the dacpac
|
||||
const dacpacFileLocation = await this.getDacpacFileLocation();
|
||||
@@ -315,9 +316,9 @@ export class ProjectsController {
|
||||
|
||||
if (databaseLocation === DatabaseReferenceLocation.differentDatabaseSameServer) {
|
||||
const databaseName = await this.getDatabaseName(dacpacFileLocation);
|
||||
await project.addDatabaseReference(dacpacFileLocation, <DatabaseReferenceLocation>databaseLocation, databaseName);
|
||||
await project.addDatabaseReference(dacpacFileLocation, <DatabaseReferenceLocation>databaseLocation, false, databaseName);
|
||||
} else {
|
||||
await project.addDatabaseReference(dacpacFileLocation, <DatabaseReferenceLocation>databaseLocation);
|
||||
await project.addDatabaseReference(dacpacFileLocation, <DatabaseReferenceLocation>databaseLocation, false);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -328,7 +329,7 @@ export class ProjectsController {
|
||||
private async getDatabaseReferenceType(): Promise<string> {
|
||||
let databaseReferenceOptions: QuickPickItem[] = [
|
||||
{
|
||||
label: constants.master
|
||||
label: constants.systemDatabase
|
||||
},
|
||||
{
|
||||
label: constants.dacpac
|
||||
@@ -347,6 +348,33 @@ export class ProjectsController {
|
||||
return input.label;
|
||||
}
|
||||
|
||||
public async getSystemDatabaseName(project: Project): Promise<SystemDatabase> {
|
||||
let databaseReferenceOptions: QuickPickItem[] = [
|
||||
{
|
||||
label: constants.master
|
||||
}
|
||||
];
|
||||
|
||||
// Azure dbs can only reference master
|
||||
if (project.getProjectTargetPlatform() !== TargetPlatform.SqlAzureV12) {
|
||||
databaseReferenceOptions.push(
|
||||
{
|
||||
label: constants.msdb
|
||||
});
|
||||
}
|
||||
|
||||
let input = await this.apiWrapper.showQuickPick(databaseReferenceOptions, {
|
||||
canPickMany: false,
|
||||
placeHolder: constants.systemDatabaseReferenceInput
|
||||
});
|
||||
|
||||
if (!input) {
|
||||
throw new Error(constants.systemDatabaseReferenceRequired);
|
||||
}
|
||||
|
||||
return input.label === constants.master ? SystemDatabase.master : SystemDatabase.msdb;
|
||||
}
|
||||
|
||||
private async getDacpacFileLocation(): Promise<Uri> {
|
||||
let fileUris = await this.apiWrapper.showOpenDialog(
|
||||
{
|
||||
|
||||
@@ -144,14 +144,28 @@ export class Project {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds reference to the appropriate master dacpac to the project
|
||||
* Adds reference to the appropriate system database dacpac to the project
|
||||
*/
|
||||
public async addMasterDatabaseReference(): Promise<void> {
|
||||
const uri = this.getMasterDacpac();
|
||||
this.addDatabaseReference(uri, DatabaseReferenceLocation.differentDatabaseSameServer, constants.master);
|
||||
public async addSystemDatabaseReference(name: SystemDatabase): Promise<void> {
|
||||
let uri: Uri;
|
||||
let dbName: string;
|
||||
if (name === SystemDatabase.master) {
|
||||
uri = this.getSystemDacpacUri(constants.masterDacpac);
|
||||
dbName = constants.master;
|
||||
} else {
|
||||
uri = this.getSystemDacpacUri(constants.msdbDacpac);
|
||||
dbName = constants.msdb;
|
||||
}
|
||||
|
||||
this.addDatabaseReference(uri, DatabaseReferenceLocation.differentDatabaseSameServer, true, dbName);
|
||||
}
|
||||
|
||||
public getMasterDacpac(): Uri {
|
||||
public getSystemDacpacUri(dacpac: string): Uri {
|
||||
let version = this.getProjectTargetPlatform();
|
||||
return Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', version, dacpac));
|
||||
}
|
||||
|
||||
public getProjectTargetPlatform(): string {
|
||||
// check for invalid DSP
|
||||
if (this.projFileXmlDoc.getElementsByTagName(constants.DSP).length !== 1 || this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes.length !== 1) {
|
||||
throw new Error(constants.invalidDataSchemaProvider);
|
||||
@@ -166,12 +180,11 @@ export class Project {
|
||||
version = version.substring(0, version.length - constants.databaseSchemaProvider.length);
|
||||
|
||||
// make sure version is valid
|
||||
console.error(Object.values(TargetPlatform));
|
||||
if (!Object.values(TargetPlatform).includes(version)) {
|
||||
throw new Error(constants.invalidDataSchemaProvider);
|
||||
}
|
||||
|
||||
return Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', version, 'master.dacpac'));
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,8 +192,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 DatabaseReferenceProjectEntry(uri, databaseLocation, databaseName);
|
||||
public async addDatabaseReference(uri: Uri, databaseLocation: DatabaseReferenceLocation, isSystemDatabase: boolean, databaseName?: string): Promise<void> {
|
||||
let databaseReferenceEntry = new DatabaseReferenceProjectEntry(uri, databaseLocation, isSystemDatabase, databaseName);
|
||||
await this.addToProjFile(databaseReferenceEntry);
|
||||
}
|
||||
|
||||
@@ -231,7 +244,7 @@ export class Project {
|
||||
private addDatabaseReferenceToProjFile(entry: DatabaseReferenceProjectEntry): void {
|
||||
const referenceNode = this.projFileXmlDoc.createElement(constants.ArtifactReference);
|
||||
referenceNode.setAttribute(constants.Condition, constants.NetCoreCondition);
|
||||
referenceNode.setAttribute(constants.Include, entry.fsUri.fsPath);
|
||||
referenceNode.setAttribute(constants.Include, entry.isSystemDatabase ? entry.fsUri.fsPath.substring(1) : entry.fsUri.fsPath); // need to remove the leading slash for system database path for build to work on Windows
|
||||
|
||||
let suppressMissingDependenciesErrorNode = this.projFileXmlDoc.createElement(constants.SuppressMissingDependenciesErrors);
|
||||
let falseTextNode = this.projFileXmlDoc.createTextNode('False');
|
||||
@@ -347,7 +360,7 @@ export class ProjectEntry {
|
||||
* Represents a database reference entry in a project file
|
||||
*/
|
||||
class DatabaseReferenceProjectEntry extends ProjectEntry {
|
||||
constructor(uri: Uri, public databaseLocation: DatabaseReferenceLocation, public name?: string) {
|
||||
constructor(uri: Uri, public databaseLocation: DatabaseReferenceLocation, public isSystemDatabase: boolean, public name?: string) {
|
||||
super(uri, '', EntryType.DatabaseReference);
|
||||
}
|
||||
}
|
||||
@@ -373,3 +386,8 @@ export enum TargetPlatform {
|
||||
Sql150 = '150',
|
||||
SqlAzureV12 = 'AzureV12'
|
||||
}
|
||||
|
||||
export enum SystemDatabase {
|
||||
master,
|
||||
msdb
|
||||
}
|
||||
|
||||
@@ -90,16 +90,33 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
const project = new Project(projFilePath);
|
||||
await project.readProjFile();
|
||||
|
||||
let uri = project.getMasterDacpac();
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '130', 'master.dacpac')).fsPath);
|
||||
let uri = project.getSystemDacpacUri(constants.masterDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '130', constants.masterDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.Sql150.toString());
|
||||
uri = project.getMasterDacpac();
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '150', 'master.dacpac')).fsPath);
|
||||
uri = project.getSystemDacpacUri(constants.masterDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '150', constants.masterDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.SqlAzureV12.toString());
|
||||
uri = project.getMasterDacpac();
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12', 'master.dacpac')).fsPath);
|
||||
uri = project.getSystemDacpacUri(constants.masterDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12',constants.masterDacpac)).fsPath);
|
||||
});
|
||||
|
||||
it('Should choose correct msdb dacpac', async function(): Promise<void> {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = new Project(projFilePath);
|
||||
await project.readProjFile();
|
||||
|
||||
let uri = project.getSystemDacpacUri(constants.msdbDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '130', constants.msdbDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.Sql150.toString());
|
||||
uri = project.getSystemDacpacUri(constants.msdbDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '150', constants.msdbDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.SqlAzureV12.toString());
|
||||
uri = project.getSystemDacpacUri(constants.msdbDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12', constants.msdbDacpac)).fsPath);
|
||||
});
|
||||
|
||||
it('Should throw error when choosing correct master dacpac if invalid DSP', async function(): Promise<void> {
|
||||
@@ -108,7 +125,7 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
await project.readProjFile();
|
||||
|
||||
project.changeDSP('invalidPlatform');
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.getMasterDacpac(), constants.invalidDataSchemaProvider);
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.getSystemDacpacUri(constants.masterDacpac), constants.invalidDataSchemaProvider);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { SqlDatabaseProjectTreeViewProvider } from '../controllers/databaseProje
|
||||
import { ProjectsController } from '../controllers/projectController';
|
||||
import { promises as fs } from 'fs';
|
||||
import { createContext, TestContext } from './testContext';
|
||||
import { Project } from '../models/project';
|
||||
import { Project, SystemDatabase } from '../models/project';
|
||||
import { DeployDatabaseDialog } from '../dialogs/deployDatabaseDialog';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
import { IDeploymentProfile, IGenerateScriptProfile } from '../models/IDeploymentProfile';
|
||||
@@ -255,6 +255,24 @@ describe('ProjectsController: add database reference operations', function (): v
|
||||
const projController = new ProjectsController(testContext.apiWrapper.object, new SqlDatabaseProjectTreeViewProvider());
|
||||
await testUtils.shouldThrowSpecificError(async () => await projController.addDatabaseReference(new Project('FakePath')), constants.databaseNameRequired);
|
||||
});
|
||||
|
||||
it('Should return the correct system database', async function (): Promise<void> {
|
||||
const projController = new ProjectsController(testContext.apiWrapper.object, new SqlDatabaseProjectTreeViewProvider());
|
||||
const projFilePath = await testUtils.createTestSqlProjFile(baselines.openProjectFileBaseline);
|
||||
const project: Project = new Project(projFilePath);
|
||||
await project.readProjFile();
|
||||
|
||||
testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ label: constants.master }));
|
||||
let systemDb = await projController.getSystemDatabaseName(project);
|
||||
should.equal(systemDb, SystemDatabase.master);
|
||||
|
||||
testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({ label: constants.msdb }));
|
||||
systemDb = await projController.getSystemDatabaseName(project);
|
||||
should.equal(systemDb, SystemDatabase.msdb);
|
||||
|
||||
testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
|
||||
await testUtils.shouldThrowSpecificError(async () => await projController.getSystemDatabaseName(project), constants.systemDatabaseReferenceRequired);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProjectsController: round trip feature with SSDT', function (): void {
|
||||
|
||||
Reference in New Issue
Block a user