Handle exclude folder for sdk style sql projects (#17826)

* handle exclude Folder for sdk style projects

* update comment

* fix tests

* cleanup

* handle nested folders

* cleanup

* addressing comments
This commit is contained in:
Kim Santiago
2021-12-14 11:29:42 -08:00
committed by GitHub
parent be933c88c0
commit 2b1acbc2c7
7 changed files with 205 additions and 21 deletions

View File

@@ -13,6 +13,7 @@
<ItemGroup>
<Folder Include="Properties" />
<Folder Include="folder1" />
<Folder Include="folder2\" />
</ItemGroup>
<ItemGroup>
<Build Include="folder1\file2.sql" />

View File

@@ -37,7 +37,7 @@ describe('Project: sqlproj content operations', function (): void {
should(project.files.filter(f => f.type === EntryType.File).length).equal(6);
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(4);
should(project.files.find(f => f.type === EntryType.Folder && f.relativePath === 'Views\\User')).not.equal(undefined); // mixed ItemGroup folder
should(project.files.find(f => f.type === EntryType.Folder && f.relativePath === 'Views\\User\\')).not.equal(undefined); // mixed ItemGroup folder
should(project.files.find(f => f.type === EntryType.File && f.relativePath === 'Views\\User\\Profile.sql')).not.equal(undefined); // mixed ItemGroup file
should(project.files.find(f => f.type === EntryType.File && f.relativePath === '..\\Test\\Test.sql')).not.equal(undefined); // mixed ItemGroup file
should(project.files.find(f => f.type === EntryType.File && f.relativePath === 'MyExternalStreamingJob.sql')).not.equal(undefined); // entry with custom attribute
@@ -843,8 +843,8 @@ describe('Project: sdk style project content operations', function (): void {
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);
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(3);
should(project.files.filter(f => f.type === EntryType.File).length).equal(17);
// SqlCmdVariables
should(Object.keys(project.sqlCmdVariables).length).equal(2);
@@ -880,7 +880,7 @@ describe('Project: sdk style project content operations', function (): void {
// 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);
should(project.files.filter(f => f.relativePath === 'folder1\\').length).equal(1);
});
it('Should handle globbing patterns listed in sqlproj', async function (): Promise<void> {
@@ -1013,7 +1013,7 @@ describe('Project: sdk style project content operations', function (): void {
projFilePath = await testUtils.createTestSqlProjFile(baselines.openSdkStyleSqlProjectBaseline);
const project = await Project.openProject(projFilePath);
const folderPath = 'Stored Procedures';
const folderPath = 'Stored Procedures\\';
const scriptPath = path.join(folderPath, 'Fake Stored Proc.sql');
const scriptContents = 'SELECT \'This is not actually a stored procedure.\'';
@@ -1023,7 +1023,7 @@ describe('Project: sdk style project content operations', function (): void {
const outsideFolderScriptPath = path.join('..', 'Other Fake Stored Proc.sql');
const outsideFolderScriptContents = 'SELECT \'This is also not actually a stored procedure.\'';
const otherFolderPath = 'OtherFolder';
const otherFolderPath = 'OtherFolder\\';
await project.addScriptItem(scriptPath, scriptContents);
await project.addScriptItem(scriptPathTagged, scriptContentsTagged, templates.externalStreamingJob);
@@ -1037,7 +1037,9 @@ describe('Project: sdk style project content operations', function (): void {
should(newProject.files.find(f => f.type === EntryType.File && f.relativePath === convertSlashesForSqlProj(scriptPathTagged))).not.equal(undefined);
should(newProject.files.find(f => f.type === EntryType.File && f.relativePath === convertSlashesForSqlProj(scriptPathTagged))?.sqlObjectType).equal(constants.ExternalStreamingJob);
should(newProject.files.find(f => f.type === EntryType.File && f.relativePath === convertSlashesForSqlProj(outsideFolderScriptPath))).not.equal(undefined);
should(newProject.files.find(f => f.type === EntryType.Folder && f.relativePath === convertSlashesForSqlProj(otherFolderPath))).not.equal(undefined);
// TODO: uncomment after add empty folder is updated
// should(newProject.files.find(f => f.type === EntryType.Folder && f.relativePath === convertSlashesForSqlProj(otherFolderPath))).not.equal(undefined);
// only the external streaming job and file outside of the project folder should have been added to the sqlproj
const projFileText = (await fs.readFile(projFilePath)).toString();
@@ -1048,6 +1050,92 @@ describe('Project: sdk style project content operations', function (): void {
should(projFileText.includes('<Folder Include="OtherFolder" />')).equal(false, projFileText);
});
it('Should handle excluding glob included folders', async function (): Promise<void> {
const testFolderPath = await testUtils.generateTestFolderPath();
projFilePath = await testUtils.createTestSqlProjFile(baselines.openSdkStyleSqlProjectBaseline, testFolderPath);
await testUtils.createDummyFileStructureWithPrePostDeployScripts(false, undefined, path.dirname(projFilePath));
const project: Project = await Project.openProject(projFilePath);
should(project.files.filter(f => f.type === EntryType.File).length).equal(17);
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(3);
// try to exclude a glob included folder
await project.exclude(project.files.find(f => f.relativePath === 'folder1\\')!);
// verify folder and contents are excluded
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(1);
should(project.files.filter(f => f.type === EntryType.File).length).equal(9);
should(project.files.find(f => f.relativePath === 'folder1\\')).equal(undefined);
// verify sqlproj has glob exclude for folder, but not for files and inner folder
const projFileText = (await fs.readFile(projFilePath)).toString();
should(projFileText.includes('<Build Remove="folder1\\**" />')).equal(true, projFileText);
should(projFileText.includes('<Build Remove="folder1\\file1.sql" />')).equal(false, projFileText);
should(projFileText.includes('<Build Remove="folder1\\nestedFolder\\**" />')).equal(false, projFileText);
});
it('Should handle excluding nested glob included folders', async function (): Promise<void> {
const testFolderPath = await testUtils.generateTestFolderPath();
projFilePath = await testUtils.createTestSqlProjFile(baselines.openSdkStyleSqlProjectBaseline, testFolderPath);
await testUtils.createDummyFileStructureWithPrePostDeployScripts(false, undefined, path.dirname(projFilePath));
const project: Project = await Project.openProject(projFilePath);
should(project.files.filter(f => f.type === EntryType.File).length).equal(17);
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(3);
// try to exclude a glob included folder
await project.exclude(project.files.find(f => f.relativePath === 'folder1\\nestedFolder\\')!);
// verify folder and contents are excluded
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(2);
should(project.files.filter(f => f.type === EntryType.File).length).equal(15);
should(project.files.find(f => f.relativePath === 'folder1\\nestedFolder\\')).equal(undefined);
// verify sqlproj has glob exclude for folder, but not for files
const projFileText = (await fs.readFile(projFilePath)).toString();
should(projFileText.includes('<Build Remove="folder1\\nestedFolder\\**" />')).equal(true, projFileText);
should(projFileText.includes('<Build Remove="folder1\\nestedFolder\\otherFile1.sql" />')).equal(false, projFileText);
});
it('Should handle excluding explicitly included folders', async function (): Promise<void> {
const testFolderPath = await testUtils.generateTestFolderPath();
projFilePath = await testUtils.createTestSqlProjFile(baselines.openSdkStyleSqlProjectWithFilesSpecifiedBaseline, testFolderPath);
await testUtils.createDummyFileStructure(false, undefined, path.dirname(projFilePath));
const project: Project = await Project.openProject(projFilePath);
should(project.files.filter(f => f.type === EntryType.File).length).equal(11);
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(2);
should(project.files.find(f => f.relativePath === 'folder1\\')!).not.equal(undefined);
should(project.files.find(f => f.relativePath === 'folder2\\')!).not.equal(undefined);
// try to exclude an explicitly included folder without trailing \ in sqlproj
await project.exclude(project.files.find(f => f.relativePath === 'folder1\\')!);
// verify folder and contents are excluded
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(1);
should(project.files.filter(f => f.type === EntryType.File).length).equal(6);
should(project.files.find(f => f.relativePath === 'folder1\\')).equal(undefined);
// try to exclude an explicitly included folder with trailing \ in sqlproj
await project.exclude(project.files.find(f => f.relativePath === 'folder2\\')!);
// verify folder and contents are excluded
should(project.files.filter(f => f.type === EntryType.Folder).length).equal(0);
should(project.files.filter(f => f.type === EntryType.File).length).equal(1);
should(project.files.find(f => f.relativePath === 'folder2\\')).equal(undefined);
// make sure both folders are removed from sqlproj and remove entry is added
const projFileText = (await fs.readFile(projFilePath)).toString();
should(projFileText.includes('<Folder Include="folder1" />')).equal(false, projFileText);
should(projFileText.includes('<Folder Include="folder2\" />')).equal(false, projFileText);
should(projFileText.includes('<Build Remove="folder1\\**" />')).equal(true, projFileText);
should(projFileText.includes('<Build Remove="folder2\\**" />')).equal(true, projFileText);
});
});
describe('Project: add SQLCMD Variables', function (): void {

View File

@@ -125,6 +125,9 @@ export async function createDummyFileStructure(createList?: boolean, list?: Uri[
* -file4.sql
* -file5.sql
* -Script.PostDeployment2.sql
* - nestedFolder
* -otherFile1.sql
* -otherFile2.sql
* - folder2
* -file1.sql
* -file2.sql
@@ -159,6 +162,14 @@ export async function createDummyFileStructureWithPrePostDeployScripts(createLis
const postdeployscript2 = path.join(testFolderPath, 'folder1', 'Script.PostDeployment2.sql');
await fs.writeFile(postdeployscript2, '');
// add nested files
await fs.mkdir(path.join(testFolderPath, 'folder1', 'nestedFolder'));
const otherfile1 = path.join(testFolderPath, 'folder1', 'nestedFolder', 'otherFile1.sql');
await fs.writeFile(otherfile1, '');
const otherfile2 = path.join(testFolderPath, 'folder1', 'nestedFolder', 'otherFile2.sql');
await fs.writeFile(otherfile2, '');
if (createList) {
list?.push(Uri.file(postdeployscript1));
list?.push(Uri.file(postdeployscript2));