mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-17 17:22:42 -05:00
Add support for showing files for glob style sql projects (#17518)
* use glob to get files for new style msbuild sdk sqlproj * add tests * cleanup * fix test * don't show bin and obj files and folders * handle other glob patterns * fix duplicate entries getting added for glob patterns in project's folder
This commit is contained in:
@@ -30,6 +30,9 @@ export let sqlProjectInvalidCollationBaseline: string;
|
||||
export let newStyleProjectSdkNodeBaseline: string;
|
||||
export let newStyleProjectSdkProjectAttributeBaseline: string;
|
||||
export let newStyleProjectSdkImportAttributeBaseline: string;
|
||||
export let openNewStyleSqlProjectBaseline: string;
|
||||
export let openNewStyleSqlProjectWithFilesSpecifiedBaseline: string;
|
||||
export let openNewStyleSqlProjectWithGlobsSpecifiedBaseline: string;
|
||||
|
||||
const baselineFolderPath = __dirname;
|
||||
|
||||
@@ -57,6 +60,9 @@ export async function loadBaselines() {
|
||||
newStyleProjectSdkNodeBaseline = await loadBaseline(baselineFolderPath, 'newStyleSqlProjectSdkNodeBaseline.xml');
|
||||
newStyleProjectSdkProjectAttributeBaseline = await loadBaseline(baselineFolderPath, 'newStyleSqlProjectSdkProjectAttributeBaseline.xml');
|
||||
newStyleProjectSdkImportAttributeBaseline = await loadBaseline(baselineFolderPath, 'newStyleSqlProjectSdkImportAttributeBaseline.xml');
|
||||
openNewStyleSqlProjectBaseline = await loadBaseline(baselineFolderPath, 'openNewStyleSqlProjectBaseline.xml');
|
||||
openNewStyleSqlProjectWithFilesSpecifiedBaseline = await loadBaseline(baselineFolderPath, 'openNewStyleSqlProjectWithFilesSpecifiedBaseline.xml');
|
||||
openNewStyleSqlProjectWithGlobsSpecifiedBaseline = await loadBaseline(baselineFolderPath, 'openNewStyleSqlProjectWithGlobsSpecifiedBaseline.xml');
|
||||
}
|
||||
|
||||
async function loadBaseline(baselineFolderPath: string, fileName: string): Promise<string> {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build">
|
||||
<Sdk Name="Microsoft.Build.Sql" Version="1.0.0" />
|
||||
<PropertyGroup>
|
||||
<Name>TestProjectName</Name>
|
||||
<ProjectGuid>{2C283C5D-9E4A-4313-8FF9-4E0CEE20B063}</ProjectGuid>
|
||||
<DSP>Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider</DSP>
|
||||
<ModelCollation>1033, CI</ModelCollation>
|
||||
</PropertyGroup>
|
||||
<Target Name="BeforeBuild">
|
||||
<Delete Files="$(BaseIntermediateOutputPath)\project.assets.json" />
|
||||
</Target>
|
||||
<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>
|
||||
<PreDeploy Include="Script.PreDeployment1.sql" />
|
||||
<None Include="Script.PreDeployment2.sql" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PostDeploy Include="Script.PostDeployment1.sql" />
|
||||
<None Include="folder1\Script.PostDeployment2.sql" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ArtifactReference Condition="'$(NetCoreBuild)' == 'true'" Include="$(NETCoreTargetsPath)\SystemDacpacs\150\master.dacpac">
|
||||
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
|
||||
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
|
||||
</ArtifactReference>
|
||||
<ArtifactReference Condition="'$(NetCoreBuild)' != 'true'" Include="$(DacPacRootPath)\Extensions\Microsoft\SQLDB\Extensions\SqlServer\150\SqlSchemas\master.dacpac">
|
||||
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
|
||||
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
|
||||
</ArtifactReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build">
|
||||
<Sdk Name="Microsoft.Build.Sql" Version="1.0.0" />
|
||||
<PropertyGroup>
|
||||
<Name>TestProjectName</Name>
|
||||
<ProjectGuid>{2C283C5D-9E4A-4313-8FF9-4E0CEE20B063}</ProjectGuid>
|
||||
<DSP>Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider</DSP>
|
||||
<ModelCollation>1033, CI</ModelCollation>
|
||||
</PropertyGroup>
|
||||
<Target Name="BeforeBuild">
|
||||
<Delete Files="$(BaseIntermediateOutputPath)\project.assets.json" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties" />
|
||||
<Folder Include="folder1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Build Include="folder1\file2.sql" />
|
||||
<Build Include="file1.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>
|
||||
<PreDeploy Include="Script.PreDeployment1.sql" />
|
||||
<None Include="Script.PreDeployment2.sql" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PostDeploy Include="Script.PostDeployment1.sql" />
|
||||
<None Include="folder1\Script.PostDeployment1.sql" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ArtifactReference Condition="'$(NetCoreBuild)' == 'true'" Include="$(NETCoreTargetsPath)\SystemDacpacs\150\master.dacpac">
|
||||
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
|
||||
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
|
||||
</ArtifactReference>
|
||||
<ArtifactReference Condition="'$(NetCoreBuild)' != 'true'" Include="$(DacPacRootPath)\Extensions\Microsoft\SQLDB\Extensions\SqlServer\150\SqlSchemas\master.dacpac">
|
||||
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
|
||||
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
|
||||
</ArtifactReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build">
|
||||
<Sdk Name="Microsoft.Build.Sql" Version="1.0.0" />
|
||||
<PropertyGroup>
|
||||
<Name>TestProjectName</Name>
|
||||
<ProjectGuid>{2C283C5D-9E4A-4313-8FF9-4E0CEE20B063}</ProjectGuid>
|
||||
<DSP>Microsoft.Data.Tools.Schema.Sql.Sql150DatabaseSchemaProvider</DSP>
|
||||
<ModelCollation>1033, CI</ModelCollation>
|
||||
</PropertyGroup>
|
||||
<Target Name="BeforeBuild">
|
||||
<Delete Files="$(BaseIntermediateOutputPath)\project.assets.json" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Build Include="..\other\folder1\file*.sql" />
|
||||
<Build Include="..\other\folder1\test?.sql" />
|
||||
<Build Include="..\other\folder2\*.sql" />
|
||||
<Build Include="folder1\*.sql" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ArtifactReference Condition="'$(NetCoreBuild)' == 'true'" Include="$(NETCoreTargetsPath)\SystemDacpacs\150\master.dacpac">
|
||||
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
|
||||
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
|
||||
</ArtifactReference>
|
||||
<ArtifactReference Condition="'$(NetCoreBuild)' != 'true'" Include="$(DacPacRootPath)\Extensions\Microsoft\SQLDB\Extensions\SqlServer\150\SqlSchemas\master.dacpac">
|
||||
<SuppressMissingDependenciesErrors>False</SuppressMissingDependenciesErrors>
|
||||
<DatabaseVariableLiteralValue>master</DatabaseVariableLiteralValue>
|
||||
</ArtifactReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -827,6 +827,95 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Project: Msbuild sdk style project content operations', function (): void {
|
||||
before(async function (): Promise<void> {
|
||||
await baselines.loadBaselines();
|
||||
});
|
||||
|
||||
beforeEach(function (): void {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('Should read project from sqlproj and files and folders by globbing', async function (): Promise<void> {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.openNewStyleSqlProjectBaseline);
|
||||
await testUtils.createDummyFileStructureWithPrePostDeployScripts(false, undefined, path.dirname(projFilePath));
|
||||
const project: Project = await Project.openProject(projFilePath);
|
||||
|
||||
// Files and folders
|
||||
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(2);
|
||||
should(project.files.filter(f => f.type === EntryType.File).length).equal(15);
|
||||
|
||||
// SqlCmdVariables
|
||||
should(Object.keys(project.sqlCmdVariables).length).equal(2);
|
||||
should(project.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase');
|
||||
should(project.sqlCmdVariables['BackupDatabaseName']).equal('MyBackupDatabase');
|
||||
|
||||
// Database references
|
||||
// should only have one database reference even though there are two master.dacpac references (1 for ADS and 1 for SSDT)
|
||||
should(project.databaseReferences.length).equal(1);
|
||||
should(project.databaseReferences[0].databaseName).containEql(constants.master);
|
||||
should(project.databaseReferences[0] instanceof SystemDatabaseReferenceProjectEntry).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.noneDeployScripts.find(f => f.type === EntryType.File && f.relativePath === 'Script.PreDeployment2.sql')).not.equal(undefined, 'File Script.PreDeployment2.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 === 'folder1\\Script.PostDeployment2.sql')).not.equal(undefined, 'File folder1\\Script.PostDeployment2.sql not read');
|
||||
});
|
||||
|
||||
it('Should handle files listed in sqlproj', async function (): Promise<void> {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.openNewStyleSqlProjectWithFilesSpecifiedBaseline);
|
||||
await testUtils.createDummyFileStructure(false, undefined, path.dirname(projFilePath));
|
||||
|
||||
const project: Project = await Project.openProject(projFilePath);
|
||||
|
||||
// Files and folders
|
||||
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(2);
|
||||
should(project.files.filter(f => f.type === EntryType.File).length).equal(11);
|
||||
|
||||
// these are also listed in the sqlproj, but there shouldn't be duplicate entries for them
|
||||
should(project.files.filter(f => f.relativePath === 'folder1\\file2.sql').length).equal(1);
|
||||
should(project.files.filter(f => f.relativePath === 'file1.sql').length).equal(1);
|
||||
should(project.files.filter(f => f.relativePath === 'folder1').length).equal(1);
|
||||
});
|
||||
|
||||
it('Should handle globbing patterns listed in sqlproj', async function (): Promise<void> {
|
||||
const testFolderPath = await testUtils.generateTestFolderPath();
|
||||
const mainProjectPath = path.join(testFolderPath, 'project');
|
||||
const otherFolderPath = path.join(testFolderPath, 'other');
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.openNewStyleSqlProjectWithGlobsSpecifiedBaseline, mainProjectPath);
|
||||
await testUtils.createDummyFileStructure(false, undefined, path.dirname(projFilePath));
|
||||
|
||||
// create files outside of project folder that are included in the project file
|
||||
await fs.mkdir(otherFolderPath);
|
||||
await testUtils.createOtherDummyFiles(otherFolderPath);
|
||||
|
||||
const project: Project = await Project.openProject(projFilePath);
|
||||
|
||||
should(project.files.filter(f => f.type === EntryType.File).length).equal(17);
|
||||
|
||||
// make sure all the correct files from the globbing patterns were included
|
||||
// ..\other\folder1\test?.sql
|
||||
should(project.files.filter(f => f.relativePath === '..\\other\\folder1\\test1.sql').length).equal(1);
|
||||
should(project.files.filter(f => f.relativePath === '..\\other\\folder1\\test2.sql').length).equal(1);
|
||||
should(project.files.filter(f => f.relativePath === '..\\other\\folder1\\testLongerName.sql').length).equal(0);
|
||||
|
||||
// ..\other\folder1\file*.sql
|
||||
should(project.files.filter(f => f.relativePath === '..\\other\\folder1\\file1.sql').length).equal(1);
|
||||
should(project.files.filter(f => f.relativePath === '..\\other\\folder1\\file2.sql').length).equal(1);
|
||||
|
||||
// ..\other\folder2\*.sql
|
||||
should(project.files.filter(f => f.relativePath === '..\\other\\folder2\\file1.sql').length).equal(1);
|
||||
should(project.files.filter(f => f.relativePath === '..\\other\\folder2\\file2.sql').length).equal(1);
|
||||
|
||||
// make sure no duplicates from folder1\*.sql
|
||||
should(project.files.filter(f => f.relativePath === 'folder1\\file1.sql').length).equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Project: add SQLCMD Variables', function (): void {
|
||||
before(async function (): Promise<void> {
|
||||
await baselines.loadBaselines();
|
||||
|
||||
@@ -115,6 +115,58 @@ export async function createDummyFileStructure(createList?: boolean, list?: Uri[
|
||||
return testFolderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* TestFolder directory structure
|
||||
* - file1.sql
|
||||
* - folder1
|
||||
* -file1.sql
|
||||
* -file2.sql
|
||||
* -file3.sql
|
||||
* -file4.sql
|
||||
* -file5.sql
|
||||
* -Script.PostDeployment2.sql
|
||||
* - folder2
|
||||
* -file1.sql
|
||||
* -file2.sql
|
||||
* -file3.sql
|
||||
* -file4.sql
|
||||
* -file5.sql
|
||||
* - file2.txt
|
||||
* - Script.PreDeployment1.sql
|
||||
* - Script.PreDeployment2.sql
|
||||
* - Script.PostDeployment1.sql
|
||||
*
|
||||
* @param createList Boolean specifying to create a list of the files and folders been created
|
||||
* @param list List of files and folders that are been created
|
||||
*/
|
||||
export async function createDummyFileStructureWithPrePostDeployScripts(createList?: boolean, list?: Uri[], testFolderPath?: string): Promise<string> {
|
||||
testFolderPath = await createDummyFileStructure(createList, list, testFolderPath);
|
||||
|
||||
// add pre-deploy scripts
|
||||
const predeployscript1 = path.join(testFolderPath, 'Script.PreDeployment1.sql');
|
||||
await fs.writeFile(predeployscript1, '');
|
||||
const predeployscript2 = path.join(testFolderPath, 'Script.PreDeployment2.sql');
|
||||
await fs.writeFile(predeployscript2, '');
|
||||
|
||||
if (createList) {
|
||||
list?.push(Uri.file(predeployscript1));
|
||||
list?.push(Uri.file(predeployscript2));
|
||||
}
|
||||
|
||||
// add post-deploy scripts
|
||||
const postdeployscript1 = path.join(testFolderPath, 'Script.PostDeployment1.sql');
|
||||
await fs.writeFile(postdeployscript1, '');
|
||||
const postdeployscript2 = path.join(testFolderPath, 'folder1', 'Script.PostDeployment2.sql');
|
||||
await fs.writeFile(postdeployscript2, '');
|
||||
|
||||
if (createList) {
|
||||
list?.push(Uri.file(postdeployscript1));
|
||||
list?.push(Uri.file(postdeployscript2));
|
||||
}
|
||||
|
||||
return testFolderPath;
|
||||
}
|
||||
|
||||
export async function createListOfFiles(filePath?: string): Promise<Uri[]> {
|
||||
let fileFolderList: Uri[] = [];
|
||||
|
||||
@@ -122,3 +174,41 @@ export async function createListOfFiles(filePath?: string): Promise<Uri[]> {
|
||||
|
||||
return fileFolderList;
|
||||
}
|
||||
|
||||
/**
|
||||
* TestFolder directory structure
|
||||
* - file1.sql
|
||||
* - folder1
|
||||
* -file1.sql
|
||||
* -file2.sql
|
||||
* -test1.sql
|
||||
* -test2.sql
|
||||
* -testLongerName.sql
|
||||
* - folder2
|
||||
* -file1.sql
|
||||
* -file2.sql
|
||||
*
|
||||
*/
|
||||
export async function createOtherDummyFiles(testFolderPath: string): Promise<string> {
|
||||
let filePath = path.join(testFolderPath, 'file1.sql');
|
||||
await fs.writeFile(filePath, '');
|
||||
|
||||
for (let dirCount = 1; dirCount <= 2; dirCount++) {
|
||||
let dirName = path.join(testFolderPath, `folder${dirCount}`);
|
||||
await fs.mkdir(dirName, { recursive: true });
|
||||
|
||||
for (let fileCount = 1; fileCount <= 2; fileCount++) {
|
||||
let fileName = path.join(dirName, `file${fileCount}.sql`);
|
||||
await fs.writeFile(fileName, '');
|
||||
}
|
||||
}
|
||||
|
||||
const test1 = path.join(testFolderPath, 'folder1', 'test1.sql');
|
||||
await fs.writeFile(test1, '');
|
||||
const test2 = path.join(testFolderPath, 'folder1', 'test2.sql');
|
||||
await fs.writeFile(test2, '');
|
||||
const testLongerName = path.join(testFolderPath, 'folder1', 'testLongerName.sql');
|
||||
await fs.writeFile(testLongerName, '');
|
||||
|
||||
return testFolderPath;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user