mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-16 09:35:36 -05:00
Initial Pre-post deployment changes (#11703)
* Update Open Project to display pre-post deployment scripts in the project tree * Address comments * Fixed tests * Update nonDeployScripts to noneDeployScripts and throw a warning message instead of informational message for prePostDeployCount
This commit is contained in:
@@ -38,6 +38,7 @@ export const newDatabaseProjectName = localize('newDatabaseProjectName', "New da
|
||||
export const sqlDatabaseProject = localize('sqlDatabaseProject', "SQL database project");
|
||||
export const yesString = localize('yesString', "Yes");
|
||||
export const noString = localize('noString', "No");
|
||||
export const okString = localize('okString', "Ok");
|
||||
export const extractTargetInput = localize('extractTargetInput', "Select folder structure for SQL files");
|
||||
export const extractDatabaseSelection = localize('extractDatabaseSelection', "Select database to import");
|
||||
export const selectString = localize('selectString', "Select");
|
||||
@@ -111,6 +112,7 @@ export const databaseSelectionRequired = localize('databaseSelectionRequired', "
|
||||
export const databaseReferenceAlreadyExists = localize('databaseReferenceAlreadyExists', "A reference to this database already exists in this project");
|
||||
export const ousiderFolderPath = localize('outsideFolderPath', "Items with absolute path outside project folder are not supported. Please make sure the paths in the project file are relative to project folder.");
|
||||
export const parentTreeItemUnknown = localize('parentTreeItemUnknown', "Cannot access parent of provided tree item");
|
||||
export const prePostDeployCount = localize('prePostDeployCount', "To successfully build, update the project to have one pre-deployment script and/or one post-deployment script");
|
||||
export function projectAlreadyOpened(path: string) { return localize('projectAlreadyOpened', "Project '{0}' is already opened.", path); }
|
||||
export function projectAlreadyExists(name: string, path: string) { return localize('projectAlreadyExists', "A project named {0} already exists in {1}.", name, path); }
|
||||
export function noFileExist(fileName: string) { return localize('noFileExist', "File {0} doesn't exist", fileName); }
|
||||
@@ -170,6 +172,9 @@ export const Properties = 'Properties';
|
||||
export const RelativeOuterPath = '..';
|
||||
export const ProjectReference = 'ProjectReference';
|
||||
export const TargetConnectionString = 'TargetConnectionString';
|
||||
export const PreDeploy = 'PreDeploy';
|
||||
export const PostDeploy = 'PostDeploy';
|
||||
export const None = 'None';
|
||||
|
||||
// SqlProj File targets
|
||||
export const NetCoreTargets = '$(NETCoreTargetsPath)\\Microsoft.Data.Tools.Schema.SqlTasks.targets';
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as utils from '../common/utils';
|
||||
import * as xmlFormat from 'xml-formatter';
|
||||
import * as os from 'os';
|
||||
|
||||
import { Uri } from 'vscode';
|
||||
import { Uri, window } from 'vscode';
|
||||
import { promises as fs } from 'fs';
|
||||
import { DataSource } from './dataSources/dataSources';
|
||||
|
||||
@@ -25,6 +25,9 @@ export class Project {
|
||||
public importedTargets: string[] = [];
|
||||
public databaseReferences: IDatabaseReferenceProjectEntry[] = [];
|
||||
public sqlCmdVariables: Record<string, string> = {};
|
||||
public preDeployScripts: ProjectEntry[] = [];
|
||||
public postDeployScripts: ProjectEntry[] = [];
|
||||
public noneDeployScripts: ProjectEntry[] = [];
|
||||
|
||||
public get projectFolderPath() {
|
||||
return Uri.file(path.dirname(this.projectFilePath)).fsPath;
|
||||
@@ -70,6 +73,32 @@ export class Project {
|
||||
this.files.push(this.createProjectEntry(folderElements[f].getAttribute(constants.Include), EntryType.Folder));
|
||||
}
|
||||
}
|
||||
|
||||
// find all pre-deployment scripts to include
|
||||
let preDeployScriptCount: number = 0;
|
||||
const preDeploy = itemGroup.getElementsByTagName(constants.PreDeploy);
|
||||
for (let pre = 0; pre < preDeploy.length; pre++) {
|
||||
this.preDeployScripts.push(this.createProjectEntry(preDeploy[pre].getAttribute(constants.Include), EntryType.File));
|
||||
preDeployScriptCount++;
|
||||
}
|
||||
|
||||
// find all post-deployment scripts to include
|
||||
let postDeployScriptCount: number = 0;
|
||||
const postDeploy = itemGroup.getElementsByTagName(constants.PostDeploy);
|
||||
for (let post = 0; post < postDeploy.length; post++) {
|
||||
this.postDeployScripts.push(this.createProjectEntry(postDeploy[post].getAttribute(constants.Include), EntryType.File));
|
||||
postDeployScriptCount++;
|
||||
}
|
||||
|
||||
if (preDeployScriptCount > 1 || postDeployScriptCount > 1) {
|
||||
window.showWarningMessage(constants.prePostDeployCount, constants.okString);
|
||||
}
|
||||
|
||||
// find all none-deployment scripts to include
|
||||
const noneItems = itemGroup.getElementsByTagName(constants.None);
|
||||
for (let n = 0; n < noneItems.length; n++) {
|
||||
this.noneDeployScripts.push(this.createProjectEntry(noneItems[n].getAttribute(constants.Include), EntryType.File));
|
||||
}
|
||||
}
|
||||
|
||||
// find all import statements to include
|
||||
|
||||
@@ -55,7 +55,12 @@ export class ProjectRootTreeItem extends BaseProjectTreeItem {
|
||||
* Processes the list of files in a project file to constructs the tree
|
||||
*/
|
||||
private construct() {
|
||||
for (const entry of this.project.files) {
|
||||
let treeItemList = this.project.files
|
||||
.concat(this.project.preDeployScripts)
|
||||
.concat(this.project.postDeployScripts)
|
||||
.concat(this.project.noneDeployScripts);
|
||||
|
||||
for (const entry of treeItemList) {
|
||||
if (entry.type !== EntryType.File && entry.relativePath.startsWith(RelativeOuterPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export let SSDTProjectBaselineWithCleanTargetAfterUpdate: string;
|
||||
export let publishProfileIntegratedSecurityBaseline: string;
|
||||
export let publishProfileSqlLoginBaseline: string;
|
||||
export let openProjectWithProjectReferencesBaseline: string;
|
||||
export let openSqlProjectWithPrePostDeploymentError: string;
|
||||
|
||||
const baselineFolderPath = __dirname;
|
||||
|
||||
@@ -35,6 +36,7 @@ export async function loadBaselines() {
|
||||
publishProfileIntegratedSecurityBaseline = await loadBaseline(baselineFolderPath, 'publishProfileIntegratedSecurityBaseline.publish.xml');
|
||||
publishProfileSqlLoginBaseline = await loadBaseline(baselineFolderPath, 'publishProfileSqlLoginBaseline.publish.xml');
|
||||
openProjectWithProjectReferencesBaseline = await loadBaseline(baselineFolderPath, 'openSqlProjectWithProjectReferenceBaseline.xml');
|
||||
openSqlProjectWithPrePostDeploymentError = await loadBaseline(baselineFolderPath, 'openSqlProjectWithPrePostDeploymentError.xml');
|
||||
}
|
||||
|
||||
async function loadBaseline(baselineFolderPath: string, fileName: string): Promise<string> {
|
||||
|
||||
@@ -94,6 +94,14 @@
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<?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="ProdDatabaseName">
|
||||
<DefaultValue>MyProdDatabase</DefaultValue>
|
||||
<Value>$(SqlCmdVar__1)</Value>
|
||||
</SqlCmdVariable>
|
||||
<SqlCmdVariable Include="BackupDatabaseName">
|
||||
<DefaultValue>MyBackupDatabase</DefaultValue>
|
||||
<Value>$(SqlCmdVar__2)</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"/>
|
||||
<PreDeploy 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>
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as should from 'should';
|
||||
import * as path from 'path';
|
||||
import * as sinon from 'sinon';
|
||||
import * as baselines from './baselines/baselines';
|
||||
import * as testUtils from './testUtils';
|
||||
import * as constants from '../common/constants';
|
||||
@@ -12,7 +13,7 @@ import * as constants from '../common/constants';
|
||||
import { promises as fs } from 'fs';
|
||||
import { Project, EntryType, TargetPlatform, SystemDatabase, DatabaseReferenceLocation, DacpacReferenceProjectEntry, SqlProjectReferenceProjectEntry } from '../models/project';
|
||||
import { exists, convertSlashesForSqlProj } from '../common/utils';
|
||||
import { Uri } from 'vscode';
|
||||
import { Uri, window } from 'vscode';
|
||||
|
||||
let projFilePath: string;
|
||||
|
||||
@@ -46,6 +47,15 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should(project.databaseReferences.length).equal(1);
|
||||
should(project.databaseReferences[0].databaseName).containEql(constants.master);
|
||||
should(project.databaseReferences[0] instanceof DacpacReferenceProjectEntry).equal(true);
|
||||
|
||||
// Pre-post deployment scripts
|
||||
should(project.preDeployScripts.length).equal(1);
|
||||
should(project.postDeployScripts.length).equal(1);
|
||||
should(project.noneDeployScripts.length).equal(2);
|
||||
should(project.preDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Script.PreDeployment1.sql')).not.equal(undefined, 'File Script.PreDeployment1.sql not read');
|
||||
should(project.postDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Script.PostDeployment1.sql')).not.equal(undefined, 'File Script.PostDeployment1.sql not read');
|
||||
should(project.noneDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Script.PreDeployment2.sql')).not.equal(undefined, 'File Script.PostDeployment2.sql not read');
|
||||
should(project.noneDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Tables\\Script.PostDeployment1.sql')).not.equal(undefined, 'File Tables\\Script.PostDeployment1.sql not read');
|
||||
});
|
||||
|
||||
it('Should read Project with Project reference from sqlproj', async function (): Promise<void> {
|
||||
@@ -61,6 +71,24 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
should(project.databaseReferences[1] instanceof SqlProjectReferenceProjectEntry).equal(true);
|
||||
});
|
||||
|
||||
it('Should throw warning message while reading Project with more than 1 pre-deploy script from sqlproj', async function (): Promise<void> {
|
||||
const stub = sinon.stub(window, 'showWarningMessage').returns(<any>Promise.resolve(constants.okString));
|
||||
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.openSqlProjectWithPrePostDeploymentError);
|
||||
const project: Project = await Project.openProject(projFilePath);
|
||||
|
||||
should(stub.calledOnce).be.true('showWarningMessage should have been called exactly once');
|
||||
should(stub.calledWith(constants.prePostDeployCount)).be.true(`showWarningMessage not called with expected message '${constants.prePostDeployCount}' Actual '${stub.getCall(0).args[0]}'`);
|
||||
|
||||
should(project.preDeployScripts.length).equal(2);
|
||||
should(project.postDeployScripts.length).equal(1);
|
||||
should(project.noneDeployScripts.length).equal(1);
|
||||
should(project.preDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Script.PreDeployment1.sql')).not.equal(undefined, 'File Script.PreDeployment1.sql not read');
|
||||
should(project.postDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Script.PostDeployment1.sql')).not.equal(undefined, 'File Script.PostDeployment1.sql not read');
|
||||
should(project.preDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Script.PreDeployment2.sql')).not.equal(undefined, 'File Script.PostDeployment2.sql not read');
|
||||
should(project.noneDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Tables\\Script.PostDeployment1.sql')).not.equal(undefined, 'File Tables\\Script.PostDeployment1.sql not read');
|
||||
});
|
||||
|
||||
it('Should add Folder and Build entries to sqlproj', async function (): Promise<void> {
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user