mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-25 01:25:36 -05:00
Change target platform of project (#12639)
* Add quick pick to select target platform for a project * add test * show current target platform and info message for new target platform * fix test failing
This commit is contained in:
@@ -69,6 +69,8 @@ export function newObjectNamePrompt(objectType: string) { return localize('newOb
|
||||
export function deleteConfirmation(toDelete: string) { return localize('deleteConfirmation', "Are you sure you want to delete {0}?", toDelete); }
|
||||
export function deleteConfirmationContents(toDelete: string) { return localize('deleteConfirmationContents', "Are you sure you want to delete {0} and all of its contents?", toDelete); }
|
||||
export function deleteReferenceConfirmation(toDelete: string) { return localize('deleteReferenceConfirmation', "Are you sure you want to delete the reference to {0}?", toDelete); }
|
||||
export function selectTargetPlatform(currentTargetPlatform: string) { return localize('selectTargetPlatform', "Current target platform: {0}. Select new target platform", currentTargetPlatform); }
|
||||
export function currentTargetPlatform(projectName: string, currentTargetPlatform: string) { return localize('currentTargetPlatform', "Target platform of the project {0} is now {1}", projectName, currentTargetPlatform); }
|
||||
|
||||
// Publish dialog strings
|
||||
|
||||
@@ -171,6 +173,7 @@ export function deployScriptExists(scriptType: string) { return localize('deploy
|
||||
export function notValidVariableName(name: string) { return localize('notValidVariableName', "The variable name '{0}' is not valid.", name); }
|
||||
export function cantAddCircularProjectReference(project: string) { return localize('cantAddCircularProjectReference', "A reference to project '{0} cannot be added. Adding this project as a reference would cause a circular dependency", project); }
|
||||
export function unableToFindSqlCmdVariable(variableName: string) { return localize('unableToFindSqlCmdVariable', "Unable to find SQLCMD variable '{0}'", variableName); }
|
||||
export function unableToFindDatabaseReference(reference: string) { return localize('unableToFindReference', "Unable to find database reference {0}", reference); }
|
||||
|
||||
// Action types
|
||||
export const deleteAction = localize('deleteAction', 'Delete');
|
||||
@@ -286,3 +289,27 @@ export const systemDbs = ['master', 'msdb', 'tempdb', 'model'];
|
||||
export const sameDatabaseExampleUsage = 'SELECT * FROM [Schema1].[Table1]';
|
||||
export function differentDbSameServerExampleUsage(db: string) { return `SELECT * FROM [${db}].[Schema1].[Table1]`; }
|
||||
export function differentDbDifferentServerExampleUsage(server: string, db: string) { return `SELECT * FROM [${server}].[${db}].[Schema1].[Table1]`; }
|
||||
|
||||
export const sqlServer2005 = 'SQL Server 2005';
|
||||
export const sqlServer2008 = 'SQL Server 2008';
|
||||
export const sqlServer2012 = 'SQL Server 2012';
|
||||
export const sqlServer2014 = 'SQL Server 2014';
|
||||
export const sqlServer2016 = 'SQL Server 2016';
|
||||
export const sqlServer2017 = 'SQL Server 2017';
|
||||
export const sqlServer2019 = 'SQL Server 2019';
|
||||
export const sqlAzure = 'Microsoft Azure SQL Database';
|
||||
|
||||
export const targetPlatformToVersion: Map<string, string> = new Map<string, string>([
|
||||
[sqlServer2005, '90'],
|
||||
[sqlServer2008, '100'],
|
||||
[sqlServer2012, '110'],
|
||||
[sqlServer2014, '120'],
|
||||
[sqlServer2016, '130'],
|
||||
[sqlServer2017, '140'],
|
||||
[sqlServer2019, '150'],
|
||||
[sqlAzure, 'AzureV12']
|
||||
]);
|
||||
|
||||
export function getTargetPlatformFromVersion(version: string): string {
|
||||
return Array.from(targetPlatformToVersion.keys()).filter(k => targetPlatformToVersion.get(k) === version)[0];
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ export default class MainController implements vscode.Disposable {
|
||||
vscode.commands.registerCommand('sqlDatabaseProjects.editProjectFile', async (node: BaseProjectTreeItem) => { await this.projectsController.editProjectFile(node); });
|
||||
vscode.commands.registerCommand('sqlDatabaseProjects.delete', async (node: BaseProjectTreeItem) => { await this.projectsController.delete(node); });
|
||||
vscode.commands.registerCommand('sqlDatabaseProjects.exclude', async (node: FileNode | FolderNode) => { await this.projectsController.exclude(node); });
|
||||
vscode.commands.registerCommand('sqlDatabaseProjects.changeTargetPlatform', async (node: BaseProjectTreeItem) => { await this.projectsController.changeTargetPlatform(node); });
|
||||
|
||||
IconPathHelper.setExtensionContext(this.extensionContext);
|
||||
|
||||
|
||||
@@ -515,6 +515,24 @@ export class ProjectsController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the project's DSP to the selected target platform
|
||||
* @param context a treeItem in a project's hierarchy, to be used to obtain a Project
|
||||
*/
|
||||
public async changeTargetPlatform(context: Project | BaseProjectTreeItem): Promise<void> {
|
||||
const project = this.getProjectFromContext(context);
|
||||
const selectedTargetPlatform = (await vscode.window.showQuickPick((Array.from(constants.targetPlatformToVersion.keys())).map(version => { return { label: version }; }),
|
||||
{
|
||||
canPickMany: false,
|
||||
placeHolder: constants.selectTargetPlatform(constants.getTargetPlatformFromVersion(project.getProjectTargetVersion()))
|
||||
}))?.label;
|
||||
|
||||
if (selectedTargetPlatform) {
|
||||
await project.changeTargetPlatform(constants.targetPlatformToVersion.get(selectedTargetPlatform)!);
|
||||
vscode.window.showInformationMessage(constants.currentTargetPlatform(project.projectFileName, constants.getTargetPlatformFromVersion(project.getProjectTargetVersion())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a database reference to the project
|
||||
* @param context a treeItem in a project's hierarchy, to be used to obtain a Project
|
||||
@@ -592,7 +610,7 @@ export class ProjectsController {
|
||||
}
|
||||
}
|
||||
|
||||
private getProjectFromContext(context: Project | BaseProjectTreeItem | WorkspaceTreeItem) {
|
||||
private getProjectFromContext(context: Project | BaseProjectTreeItem | WorkspaceTreeItem): Project {
|
||||
if ('element' in context) {
|
||||
return context.element.project;
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ export class AddDatabaseReferenceDialog {
|
||||
});
|
||||
|
||||
// only master is a valid system db reference for projects targetting Azure
|
||||
if (this.project.getProjectTargetPlatform().toLowerCase().includes('azure')) {
|
||||
if (this.project.getProjectTargetVersion().toLowerCase().includes('azure')) {
|
||||
this.systemDatabaseDropdown.values?.splice(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -326,13 +326,33 @@ export class Project {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the compat level of the project
|
||||
* Just used in tests right now, but can be used later if this functionality is added to the UI
|
||||
* @param compatLevel compat level of project
|
||||
* Set the target platform of the project
|
||||
* @param newTargetPlatform compat level of project
|
||||
*/
|
||||
public changeDSP(compatLevel: string): void {
|
||||
const newDSP = `${constants.MicrosoftDatatoolsSchemaSqlSql}${compatLevel}${constants.databaseSchemaProvider}`;
|
||||
this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes[0].nodeValue = newDSP;
|
||||
public async changeTargetPlatform(compatLevel: string): Promise<void> {
|
||||
if (this.getProjectTargetVersion() !== compatLevel) {
|
||||
const newDSP = `${constants.MicrosoftDatatoolsSchemaSqlSql}${compatLevel}${constants.databaseSchemaProvider}`;
|
||||
this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes[0].data = newDSP;
|
||||
this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes[0].nodeValue = newDSP;
|
||||
|
||||
// update any system db references
|
||||
const systemDbReferences = this.databaseReferences.filter(r => r instanceof SystemDatabaseReferenceProjectEntry) as SystemDatabaseReferenceProjectEntry[];
|
||||
if (systemDbReferences.length > 0) {
|
||||
systemDbReferences.forEach((r) => {
|
||||
// remove old entry in sqlproj
|
||||
this.removeDatabaseReferenceFromProjFile(r);
|
||||
|
||||
// update uris to point to the correct dacpacs for the target platform
|
||||
r.fsUri = this.getSystemDacpacUri(`${r.databaseName}.dacpac`);
|
||||
r.ssdtUri = this.getSystemDacpacSsdtUri(`${r.databaseName}.dacpac`);
|
||||
|
||||
// add updated system db reference to sqlproj
|
||||
this.addDatabaseReferenceToProjFile(r);
|
||||
});
|
||||
}
|
||||
|
||||
await this.serializeToProjFile(this.projFileXmlDoc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,26 +371,32 @@ export class Project {
|
||||
}
|
||||
|
||||
const systemDatabaseReferenceProjectEntry = new SystemDatabaseReferenceProjectEntry(uri, ssdtUri, <string>settings.databaseName, settings.suppressMissingDependenciesErrors);
|
||||
|
||||
// check if reference to this database already exists
|
||||
if (this.databaseReferenceExists(systemDatabaseReferenceProjectEntry)) {
|
||||
throw new Error(constants.databaseReferenceAlreadyExists);
|
||||
}
|
||||
|
||||
await this.addToProjFile(systemDatabaseReferenceProjectEntry);
|
||||
}
|
||||
|
||||
public getSystemDacpacUri(dacpac: string): Uri {
|
||||
let version = this.getProjectTargetPlatform();
|
||||
let version = this.getProjectTargetVersion();
|
||||
return Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', version, dacpac));
|
||||
}
|
||||
|
||||
public getSystemDacpacSsdtUri(dacpac: string): Uri {
|
||||
let version = this.getProjectTargetPlatform();
|
||||
let version = this.getProjectTargetVersion();
|
||||
return Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', version, 'SqlSchemas', dacpac));
|
||||
}
|
||||
|
||||
public getProjectTargetPlatform(): string {
|
||||
public getProjectTargetVersion(): 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);
|
||||
}
|
||||
|
||||
let dsp: string = this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes[0].nodeValue;
|
||||
let dsp: string = this.projFileXmlDoc.getElementsByTagName(constants.DSP)[0].childNodes[0].data;
|
||||
|
||||
// get version from dsp, which is a string like Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider
|
||||
// remove part before the number
|
||||
@@ -379,7 +405,7 @@ export class Project {
|
||||
version = version.substring(0, version.length - constants.databaseSchemaProvider.length);
|
||||
|
||||
// make sure version is valid
|
||||
if (!Object.values(TargetPlatform).includes(version)) {
|
||||
if (!Array.from(constants.targetPlatformToVersion.values()).includes(version)) {
|
||||
throw new Error(constants.invalidDataSchemaProvider);
|
||||
}
|
||||
|
||||
@@ -393,6 +419,12 @@ export class Project {
|
||||
*/
|
||||
public async addDatabaseReference(settings: IDacpacReferenceSettings): Promise<void> {
|
||||
const databaseReferenceEntry = new DacpacReferenceProjectEntry(settings);
|
||||
|
||||
// check if reference to this database already exists
|
||||
if (this.databaseReferenceExists(databaseReferenceEntry)) {
|
||||
throw new Error(constants.databaseReferenceAlreadyExists);
|
||||
}
|
||||
|
||||
await this.addToProjFile(databaseReferenceEntry);
|
||||
}
|
||||
|
||||
@@ -546,7 +578,7 @@ export class Project {
|
||||
}
|
||||
|
||||
if (!deleted) {
|
||||
throw new Error(constants.unableToFindSqlCmdVariable(databaseReferenceEntry.databaseName));
|
||||
throw new Error(constants.unableToFindDatabaseReference(databaseReferenceEntry.databaseName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,7 +590,6 @@ export class Project {
|
||||
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);
|
||||
@@ -569,11 +600,6 @@ export class Project {
|
||||
}
|
||||
|
||||
private addDatabaseReferenceToProjFile(entry: IDatabaseReferenceProjectEntry): void {
|
||||
// check if reference to this database already exists
|
||||
if (this.databaseReferenceExists(entry)) {
|
||||
throw new Error(constants.databaseReferenceAlreadyExists);
|
||||
}
|
||||
|
||||
if (entry instanceof SystemDatabaseReferenceProjectEntry) {
|
||||
this.addSystemDatabaseReferenceToProjFile(<SystemDatabaseReferenceProjectEntry>entry);
|
||||
} else if (entry instanceof SqlProjectReferenceProjectEntry) {
|
||||
@@ -582,12 +608,14 @@ export class Project {
|
||||
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());
|
||||
this.addDatabaseReferenceChildren(referenceNode, entry);
|
||||
this.findOrCreateItemGroup(constants.ArtifactReference).appendChild(referenceNode);
|
||||
}
|
||||
|
||||
if (!this.databaseReferenceExists(entry)) {
|
||||
this.databaseReferences.push(entry);
|
||||
}
|
||||
}
|
||||
@@ -987,17 +1015,6 @@ export enum DatabaseReferenceLocation {
|
||||
differentDatabaseDifferentServer
|
||||
}
|
||||
|
||||
export enum TargetPlatform {
|
||||
Sql90 = '90',
|
||||
Sql100 = '100',
|
||||
Sql110 = '110',
|
||||
Sql120 = '120',
|
||||
Sql130 = '130',
|
||||
Sql140 = '140',
|
||||
Sql150 = '150',
|
||||
SqlAzureV12 = 'AzureV12'
|
||||
}
|
||||
|
||||
export enum SystemDatabase {
|
||||
master,
|
||||
msdb
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as testUtils from './testUtils';
|
||||
import * as constants from '../common/constants';
|
||||
|
||||
import { promises as fs } from 'fs';
|
||||
import { Project, EntryType, TargetPlatform, SystemDatabase, SystemDatabaseReferenceProjectEntry, SqlProjectReferenceProjectEntry } from '../models/project';
|
||||
import { Project, EntryType, SystemDatabase, SystemDatabaseReferenceProjectEntry, SqlProjectReferenceProjectEntry } from '../models/project';
|
||||
import { exists, convertSlashesForSqlProj } from '../common/utils';
|
||||
import { Uri, window } from 'vscode';
|
||||
|
||||
@@ -144,13 +144,13 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '150', constants.masterDacpac)).fsPath);
|
||||
should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '150', 'SqlSchemas', constants.masterDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.Sql130.toString());
|
||||
project.changeTargetPlatform(constants.targetPlatformToVersion.get(constants.sqlServer2016)!);
|
||||
uri = project.getSystemDacpacUri(constants.masterDacpac);
|
||||
ssdtUri = project.getSystemDacpacSsdtUri(constants.masterDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '130', constants.masterDacpac)).fsPath);
|
||||
should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '130', 'SqlSchemas', constants.masterDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.SqlAzureV12.toString());
|
||||
project.changeTargetPlatform(constants.targetPlatformToVersion.get(constants.sqlAzure)!);
|
||||
uri = project.getSystemDacpacUri(constants.masterDacpac);
|
||||
ssdtUri = project.getSystemDacpacSsdtUri(constants.masterDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12', constants.masterDacpac)).fsPath);
|
||||
@@ -166,13 +166,13 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '150', constants.msdbDacpac)).fsPath);
|
||||
should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '150', 'SqlSchemas', constants.msdbDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.Sql130.toString());
|
||||
project.changeTargetPlatform(constants.targetPlatformToVersion.get(constants.sqlServer2016)!);
|
||||
uri = project.getSystemDacpacUri(constants.msdbDacpac);
|
||||
ssdtUri = project.getSystemDacpacSsdtUri(constants.msdbDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', '130', constants.msdbDacpac)).fsPath);
|
||||
should.equal(ssdtUri.fsPath, Uri.parse(path.join('$(DacPacRootPath)', 'Extensions', 'Microsoft', 'SQLDB', 'Extensions', 'SqlServer', '130', 'SqlSchemas', constants.msdbDacpac)).fsPath);
|
||||
|
||||
project.changeDSP(TargetPlatform.SqlAzureV12.toString());
|
||||
project.changeTargetPlatform(constants.targetPlatformToVersion.get(constants.sqlAzure)!);
|
||||
uri = project.getSystemDacpacUri(constants.msdbDacpac);
|
||||
ssdtUri = project.getSystemDacpacSsdtUri(constants.msdbDacpac);
|
||||
should.equal(uri.fsPath, Uri.parse(path.join('$(NETCoreTargetsPath)', 'SystemDacpacs', 'AzureV12', constants.msdbDacpac)).fsPath);
|
||||
@@ -183,7 +183,7 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
project.changeDSP('invalidPlatform');
|
||||
project.changeTargetPlatform('invalidPlatform');
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.getSystemDacpacUri(constants.masterDacpac), constants.invalidDataSchemaProvider);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { SqlDatabaseProjectTreeViewProvider } from '../controllers/databaseProje
|
||||
import { ProjectsController } from '../controllers/projectController';
|
||||
import { promises as fs } from 'fs';
|
||||
import { createContext, TestContext, mockDacFxResult } from './testContext';
|
||||
import { Project, reservedProjectFolders, SystemDatabase, FileProjectEntry } from '../models/project';
|
||||
import { Project, reservedProjectFolders, SystemDatabase, FileProjectEntry, SystemDatabaseReferenceProjectEntry } from '../models/project';
|
||||
import { PublishDatabaseDialog } from '../dialogs/publishDatabaseDialog';
|
||||
import { IPublishSettings, IGenerateScriptSettings } from '../models/IPublishSettings';
|
||||
import { exists } from '../common/utils';
|
||||
@@ -253,7 +253,7 @@ describe('ProjectsController', function (): void {
|
||||
proj.addProjectReference({
|
||||
projectName: 'project1',
|
||||
projectGuid: '',
|
||||
projectRelativePath: vscode.Uri.file(path.join('..','project1', 'project1.sqlproj')),
|
||||
projectRelativePath: vscode.Uri.file(path.join('..', 'project1', 'project1.sqlproj')),
|
||||
suppressMissingDependenciesErrors: false
|
||||
});
|
||||
|
||||
@@ -335,6 +335,24 @@ describe('ProjectsController', function (): void {
|
||||
await projController.addItemPrompt(project, '', templates.postDeployScript);
|
||||
should(project.postDeployScripts.length).equal(1, 'Post deploy script should be successfully added');
|
||||
});
|
||||
|
||||
it('Should change target platform', async function (): Promise<void> {
|
||||
sinon.stub(vscode.window, 'showQuickPick').resolves({ label: constants.sqlAzure });
|
||||
|
||||
const projController = new ProjectsController(new SqlDatabaseProjectTreeViewProvider());
|
||||
const sqlProjPath = await testUtils.createTestSqlProjFile(baselines.openProjectFileBaseline);
|
||||
const project = await projController.openProject(vscode.Uri.file(sqlProjPath));
|
||||
should(project.getProjectTargetVersion()).equal(constants.targetPlatformToVersion.get(constants.sqlServer2019));
|
||||
should(project.databaseReferences.length).equal(1, 'Project should have one database reference to master');
|
||||
should(project.databaseReferences[0].fsUri.fsPath).containEql(constants.targetPlatformToVersion.get(constants.sqlServer2019));
|
||||
should((<SystemDatabaseReferenceProjectEntry>project.databaseReferences[0]).ssdtUri.fsPath).containEql(constants.targetPlatformToVersion.get(constants.sqlServer2019));
|
||||
|
||||
await projController.changeTargetPlatform(project);
|
||||
should(project.getProjectTargetVersion()).equal(constants.targetPlatformToVersion.get(constants.sqlAzure));
|
||||
// verify system db reference got updated too
|
||||
should(project.databaseReferences[0].fsUri.fsPath).containEql(constants.targetPlatformToVersion.get(constants.sqlAzure));
|
||||
should((<SystemDatabaseReferenceProjectEntry>project.databaseReferences[0]).ssdtUri.fsPath).containEql(constants.targetPlatformToVersion.get(constants.sqlAzure));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Publishing and script generation', function (): void {
|
||||
|
||||
Reference in New Issue
Block a user