Support for adding new SQLCMD variables to a project (#12009)

This commit is contained in:
Kim Santiago
2020-09-02 11:22:36 -07:00
committed by GitHub
parent 2d33c8dd82
commit f174dc1c77
8 changed files with 285 additions and 58 deletions

View File

@@ -20,6 +20,7 @@ export let publishProfileIntegratedSecurityBaseline: string;
export let publishProfileSqlLoginBaseline: string;
export let openProjectWithProjectReferencesBaseline: string;
export let openSqlProjectWithPrePostDeploymentError: string;
export let openSqlProjectWithAdditionalSqlCmdVariablesBaseline: string;
const baselineFolderPath = __dirname;
@@ -37,6 +38,7 @@ export async function loadBaselines() {
publishProfileSqlLoginBaseline = await loadBaseline(baselineFolderPath, 'publishProfileSqlLoginBaseline.publish.xml');
openProjectWithProjectReferencesBaseline = await loadBaseline(baselineFolderPath, 'openSqlProjectWithProjectReferenceBaseline.xml');
openSqlProjectWithPrePostDeploymentError = await loadBaseline(baselineFolderPath, 'openSqlProjectWithPrePostDeploymentError.xml');
openSqlProjectWithAdditionalSqlCmdVariablesBaseline = await loadBaseline(baselineFolderPath, 'openSqlProjectWithAdditionalSqlCmdVariablesBaseline.xml');
}
async function loadBaseline(baselineFolderPath: string, fileName: string): Promise<string> {

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<Name>TestProjectName</Name>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>4.1</ProjectVersion>
<ProjectGuid>{BA5EBA11-C0DE-5EA7-ACED-BABB1E70A575}</ProjectGuid>
<DSP>Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider</DSP>
<OutputType>Database</OutputType>
<RootPath>
</RootPath>
<RootNamespace>TestProjectName</RootNamespace>
<AssemblyName>TestProjectName</AssemblyName>
<ModelCollation>1033, CI</ModelCollation>
<DefaultFileStructure>BySchemaAndSchemaType</DefaultFileStructure>
<DeployToDatabase>True</DeployToDatabase>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetLanguage>CS</TargetLanguage>
<AppDesignerFolder>Properties</AppDesignerFolder>
<SqlServerVerification>False</SqlServerVerification>
<IncludeCompositeObjects>True</IncludeCompositeObjects>
<TargetDatabaseSet>True</TargetDatabaseSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
<BuildScriptName>$(MSBuildProjectName).sql</BuildScriptName>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
<BuildScriptName>$(MSBuildProjectName).sql</BuildScriptName>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<!-- Default to the v11.0 targets path if the targets file for the current VS version is not found -->
<SSDTExists Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets')">True</SSDTExists>
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
</PropertyGroup>
<Import Condition="'$(NetCoreBuild)' == 'true'" Project="$(NETCoreTargetsPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets"/>
<Import Condition="'$(NetCoreBuild)' != 'true' AND '$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets"/>
<Import Condition="'$(NetCoreBuild)' != 'true' AND '$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets"/>
<ItemGroup>
<PackageReference Condition="'$(NetCoreBuild)' == 'true'" Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties"/>
<Folder Include="Tables"/>
<Folder Include="Views"/>
<Folder Include="Views\Maintenance"/>
</ItemGroup>
<ItemGroup>
<Build Include="Tables\Users.sql"/>
<Build Include="Tables\Action History.sql"/>
<Build Include="Views\Maintenance\Database Performance.sql"/>
<Build Include="..\Test\Test.sql"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Views\User"/>
<Build Include="Views\User\Profile.sql"/>
</ItemGroup>
<ItemGroup>
<SqlCmdVariable Include="BackupDatabaseName">
<DefaultValue>MyBackupDatabase</DefaultValue>
<Value>$(SqlCmdVar__2)</Value>
</SqlCmdVariable>
<SqlCmdVariable Include="TestDatabaseName">
<DefaultValue>TestDb</DefaultValue>
<Value>$(SqlCmdVar__3)</Value>
</SqlCmdVariable>
<SqlCmdVariable Include="ProdDatabaseName">
<DefaultValue>NewProdName</DefaultValue>
<Value>$(SqlCmdVar__4)</Value>
</SqlCmdVariable>
</ItemGroup>
<ItemGroup>
<ArtifactReference Condition="'$(NetCoreBuild)' == 'true'" Include="$(NETCoreTargetsPath)\SystemDacpacs\130\master.dacpac">
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
</ArtifactReference>
<ArtifactReference Condition="'$(NetCoreBuild)' != 'true'" Include="$(DacPacRootPath)\Extensions\Microsoft\SQLDB\Extensions\SqlServer\130\SqlSchemas\master.dacpac">
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
</ArtifactReference>
</ItemGroup>
<ItemGroup>
<PreDeploy Include="Script.PreDeployment1.sql"/>
<None Include="Script.PreDeployment2.sql"/>
</ItemGroup>
<ItemGroup>
<PostDeploy Include="Script.PostDeployment1.sql"/>
<None Include="Tables\Script.PostDeployment1.sql"/>
</ItemGroup>
<Target Name="AfterClean">
<Delete Files="$(BaseIntermediateOutputPath)\project.assets.json"/>
</Target>
</Project>

View File

@@ -291,6 +291,31 @@ describe('Project: sqlproj content operations', function (): void {
});
});
describe('Project: add SQLCMD Variables', function (): void {
before(async function (): Promise<void> {
await baselines.loadBaselines();
});
it('Should update .sqlproj with new sqlcmd variables', async function (): Promise<void> {
projFilePath = await testUtils.createTestSqlProjFile(baselines.openProjectFileBaseline);
const project = await Project.openProject(projFilePath);
should(Object.keys(project.sqlCmdVariables).length).equal(2);
// add a new variable
await project.addSqlCmdVariable('TestDatabaseName', 'TestDb');
// add a variable with the same name as an existing sqlcmd variable and the old entry should be replaced with the new one
await project.addSqlCmdVariable('ProdDatabaseName', 'NewProdName');
should(Object.keys(project.sqlCmdVariables).length).equal(3);
should(project.sqlCmdVariables['TestDatabaseName']).equal('TestDb');
should(project.sqlCmdVariables['ProdDatabaseName']).equal('NewProdName', 'ProdDatabaseName value should have been updated to the new value');
const projFileText = (await fs.readFile(projFilePath)).toString();
should(projFileText).equal(baselines.openSqlProjectWithAdditionalSqlCmdVariablesBaseline.trim());
});
});
describe('Project: round trip updates', function (): void {
before(async function (): Promise<void> {
await baselines.loadBaselines();

View File

@@ -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, ProjectEntry, reservedProjectFolders, SystemDatabase } from '../models/project';
import { Project, reservedProjectFolders, SystemDatabase, FileProjectEntry } from '../models/project';
import { PublishDatabaseDialog } from '../dialogs/publishDatabaseDialog';
import { IPublishSettings, IGenerateScriptSettings } from '../models/IPublishSettings';
import { exists } from '../common/utils';
@@ -584,7 +584,7 @@ describe.skip('ProjectsController: round trip feature with SSDT', function (): v
});
async function setupDeleteExcludeTest(proj: Project): Promise<[ProjectEntry, ProjectRootTreeItem]> {
async function setupDeleteExcludeTest(proj: Project): Promise<[FileProjectEntry, ProjectRootTreeItem]> {
await proj.addFolderItem('UpperFolder');
await proj.addFolderItem('UpperFolder/LowerFolder');
const scriptEntry = await proj.addScriptItem('UpperFolder/LowerFolder/someScript.sql', 'not a real script');

View File

@@ -52,19 +52,19 @@ describe.skip('Project Tree tests', function (): void {
// nested entries before explicit top-level folder entry
// also, ordering of files/folders at all levels
proj.files.push(proj.createProjectEntry(path.join('someFolder', 'bNestedTest.sql'), EntryType.File));
proj.files.push(proj.createProjectEntry(path.join('someFolder', 'bNestedFolder'), EntryType.Folder));
proj.files.push(proj.createProjectEntry(path.join('someFolder', 'aNestedTest.sql'), EntryType.File));
proj.files.push(proj.createProjectEntry(path.join('someFolder', 'aNestedFolder'), EntryType.Folder));
proj.files.push(proj.createProjectEntry('someFolder', EntryType.Folder));
proj.files.push(proj.createFileProjectEntry(path.join('someFolder', 'bNestedTest.sql'), EntryType.File));
proj.files.push(proj.createFileProjectEntry(path.join('someFolder', 'bNestedFolder'), EntryType.Folder));
proj.files.push(proj.createFileProjectEntry(path.join('someFolder', 'aNestedTest.sql'), EntryType.File));
proj.files.push(proj.createFileProjectEntry(path.join('someFolder', 'aNestedFolder'), EntryType.Folder));
proj.files.push(proj.createFileProjectEntry('someFolder', EntryType.Folder));
// duplicate files
proj.files.push(proj.createProjectEntry('duplicate.sql', EntryType.File));
proj.files.push(proj.createProjectEntry('duplicate.sql', EntryType.File));
proj.files.push(proj.createFileProjectEntry('duplicate.sql', EntryType.File));
proj.files.push(proj.createFileProjectEntry('duplicate.sql', EntryType.File));
// duplicate folders
proj.files.push(proj.createProjectEntry('duplicateFolder', EntryType.Folder));
proj.files.push(proj.createProjectEntry('duplicateFolder', EntryType.Folder));
proj.files.push(proj.createFileProjectEntry('duplicateFolder', EntryType.Folder));
proj.files.push(proj.createFileProjectEntry('duplicateFolder', EntryType.Folder));
const tree = new ProjectRootTreeItem(proj);
should(tree.children.map(x => x.uri.path)).deepEqual([
@@ -100,9 +100,9 @@ describe.skip('Project Tree tests', function (): void {
// nested entries before explicit top-level folder entry
// also, ordering of files/folders at all levels
proj.files.push(proj.createProjectEntry('someFolder1\\MyNestedFolder1\\MyFile1.sql', EntryType.File));
proj.files.push(proj.createProjectEntry('someFolder1\\MyNestedFolder2', EntryType.Folder));
proj.files.push(proj.createProjectEntry('someFolder1\\MyFile2.sql', EntryType.File));
proj.files.push(proj.createFileProjectEntry('someFolder1\\MyNestedFolder1\\MyFile1.sql', EntryType.File));
proj.files.push(proj.createFileProjectEntry('someFolder1\\MyNestedFolder2', EntryType.Folder));
proj.files.push(proj.createFileProjectEntry('someFolder1\\MyFile2.sql', EntryType.File));
const tree = new ProjectRootTreeItem(proj);
should(tree.children.map(x => x.uri.path)).deepEqual([
@@ -123,9 +123,9 @@ describe.skip('Project Tree tests', function (): void {
// nested entries before explicit top-level folder entry
// also, ordering of files/folders at all levels
proj.files.push(proj.createProjectEntry('..\\someFolder1\\MyNestedFolder1\\MyFile1.sql', EntryType.File));
proj.files.push(proj.createProjectEntry('..\\..\\someFolder2\\MyFile2.sql', EntryType.File));
proj.files.push(proj.createProjectEntry('..\\..\\someFolder3', EntryType.Folder)); // folder should not be counted (same as SSDT)
proj.files.push(proj.createFileProjectEntry('..\\someFolder1\\MyNestedFolder1\\MyFile1.sql', EntryType.File));
proj.files.push(proj.createFileProjectEntry('..\\..\\someFolder2\\MyFile2.sql', EntryType.File));
proj.files.push(proj.createFileProjectEntry('..\\..\\someFolder3', EntryType.Folder)); // folder should not be counted (same as SSDT)
const tree = new ProjectRootTreeItem(proj);
should(tree.children.map(x => x.uri.path)).deepEqual([