diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index 2e38926cb7..2b5b20c10f 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -564,7 +564,12 @@ export enum DatabaseProjectItemType { reference = 'databaseProject.itemType.reference', dataSourceRoot = 'databaseProject.itemType.dataSourceRoot', sqlcmdVariablesRoot = 'databaseProject.itemType.sqlcmdVariablesRoot', - sqlcmdVariable = 'databaseProject.itemType.sqlcmdVariable' + sqlcmdVariable = 'databaseProject.itemType.sqlcmdVariable', + preDeploy = 'databaseProject.itemType.file.preDeploymentScript', + postDeploy = 'databaseProject.itemType.file.postDeployScript', + none = 'databaseProject.itemType.file.noneFile', + sqlObjectFile = 'databaseProject.itemType.file.sqlObjectScript', + publishProfile = 'databaseProject.itemType.file.publishProfile' } // AutoRest diff --git a/extensions/sql-database-projects/src/models/tree/fileFolderTreeItem.ts b/extensions/sql-database-projects/src/models/tree/fileFolderTreeItem.ts index fb058a3081..11eca5be8c 100644 --- a/extensions/sql-database-projects/src/models/tree/fileFolderTreeItem.ts +++ b/extensions/sql-database-projects/src/models/tree/fileFolderTreeItem.ts @@ -38,7 +38,7 @@ export class FolderNode extends BaseProjectTreeItem { /** * Node representing a file in a project */ -export class FileNode extends BaseProjectTreeItem { +export abstract class FileNode extends BaseProjectTreeItem { public fileSystemUri: vscode.Uri; constructor(filePath: vscode.Uri, sqlprojUri: vscode.Uri) { @@ -65,7 +65,16 @@ export class FileNode extends BaseProjectTreeItem { } } -export class ExternalStreamingJobFileNode extends FileNode { +export class SqlObjectFileNode extends FileNode { + public override get treeItem(): vscode.TreeItem { + const treeItem = super.treeItem; + treeItem.contextValue = DatabaseProjectItemType.sqlObjectFile; + + return treeItem; + } +} + +export class ExternalStreamingJobFileNode extends SqlObjectFileNode { public override get treeItem(): vscode.TreeItem { const treeItem = super.treeItem; treeItem.contextValue = DatabaseProjectItemType.externalStreamingJob; @@ -74,7 +83,7 @@ export class ExternalStreamingJobFileNode extends FileNode { } } -export class TableFileNode extends FileNode { +export class TableFileNode extends SqlObjectFileNode { public override get treeItem(): vscode.TreeItem { const treeItem = super.treeItem; treeItem.contextValue = DatabaseProjectItemType.table; @@ -83,6 +92,42 @@ export class TableFileNode extends FileNode { } } +export class PreDeployNode extends FileNode { + public override get treeItem(): vscode.TreeItem { + const treeItem = super.treeItem; + treeItem.contextValue = DatabaseProjectItemType.preDeploy; + + return treeItem; + } +} + +export class PostDeployNode extends FileNode { + public override get treeItem(): vscode.TreeItem { + const treeItem = super.treeItem; + treeItem.contextValue = DatabaseProjectItemType.postDeploy; + + return treeItem; + } +} + +export class NoneNode extends FileNode { + public override get treeItem(): vscode.TreeItem { + const treeItem = super.treeItem; + treeItem.contextValue = DatabaseProjectItemType.none; + + return treeItem; + } +} + +export class PublishProfileNode extends FileNode { + public override get treeItem(): vscode.TreeItem { + const treeItem = super.treeItem; + treeItem.contextValue = DatabaseProjectItemType.publishProfile; + + return treeItem; + } +} + /** * Compares two folder/file tree nodes so that folders come before files, then alphabetically * @param a a folder or file tree node diff --git a/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts b/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts index 3111aa511f..7cc31ee092 100644 --- a/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts +++ b/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts @@ -62,23 +62,32 @@ export class ProjectRootTreeItem extends BaseProjectTreeItem { * Processes the list of files in a project file to constructs the tree */ private construct() { - let treeItemList = this.project.files - .concat(this.project.preDeployScripts) - .concat(this.project.postDeployScripts) - .concat(this.project.noneDeployScripts) - .concat(this.project.publishProfiles); + // pre deploy scripts + for (const preDeployEntry of this.project.preDeployScripts) { + const newNode = new fileTree.PreDeployNode(preDeployEntry.fsUri, this.projectFileUri); + this.addNode(newNode, preDeployEntry); + } - for (const entry of treeItemList) { - if (entry.type !== EntryType.File && entry.relativePath.startsWith(RelativeOuterPath)) { - continue; - } + // post deploy scripts + for (const postDeployEntry of this.project.postDeployScripts) { + const newNode = new fileTree.PostDeployNode(postDeployEntry.fsUri, this.projectFileUri); + this.addNode(newNode, postDeployEntry); + } - const parentNode = this.getEntryParentNode(entry); + // none scripts + for (const noneEntry of this.project.noneDeployScripts) { + const newNode = new fileTree.NoneNode(noneEntry.fsUri, this.projectFileUri); + this.addNode(newNode, noneEntry); + } - if (Object.keys(parentNode.fileChildren).includes(path.basename(entry.fsUri.path))) { - continue; // ignore duplicate entries - } + // publish profiles + for (const publishProfile of this.project.publishProfiles) { + const newNode = new fileTree.PublishProfileNode(publishProfile.fsUri, this.projectFileUri); + this.addNode(newNode, publishProfile); + } + // sql object scripts and folders + for (const entry of this.project.files) { let newNode: fileTree.FolderNode | fileTree.FileNode; switch (entry.type) { @@ -89,7 +98,7 @@ export class ProjectRootTreeItem extends BaseProjectTreeItem { newNode = new fileTree.TableFileNode(entry.fsUri, this.projectFileUri); } else { - newNode = new fileTree.FileNode(entry.fsUri, this.projectFileUri); + newNode = new fileTree.SqlObjectFileNode(entry.fsUri, this.projectFileUri); } break; @@ -100,10 +109,25 @@ export class ProjectRootTreeItem extends BaseProjectTreeItem { throw new Error(`Unknown EntryType: '${entry.type}'`); } - parentNode.fileChildren[path.basename(entry.fsUri.path)] = newNode; + this.addNode(newNode, entry); } } + private addNode(newNode: fileTree.FileNode | fileTree.FolderNode, entry: FileProjectEntry): void { + // Don't add external folders + if (entry.type !== EntryType.File && entry.relativePath.startsWith(RelativeOuterPath)) { + return; + } + + const parentNode = this.getEntryParentNode(entry); + + if (Object.keys(parentNode.fileChildren).includes(path.basename(entry.fsUri.path))) { + return; // ignore duplicate entries + } + + parentNode.fileChildren[path.basename(entry.fsUri.path)] = newNode; + } + /** * Gets the immediate parent tree node for an entry in a project file */ diff --git a/extensions/sql-database-projects/src/test/projectTree.test.ts b/extensions/sql-database-projects/src/test/projectTree.test.ts index 5039ccab1a..44fd5ef5e4 100644 --- a/extensions/sql-database-projects/src/test/projectTree.test.ts +++ b/extensions/sql-database-projects/src/test/projectTree.test.ts @@ -9,7 +9,7 @@ import * as os from 'os'; import * as path from 'path'; import { Project } from '../models/project'; -import { FolderNode, FileNode, sortFileFolderNodes } from '../models/tree/fileFolderTreeItem'; +import { FolderNode, FileNode, sortFileFolderNodes, SqlObjectFileNode } from '../models/tree/fileFolderTreeItem'; import { ProjectRootTreeItem } from '../models/tree/projectTreeItem'; import { DatabaseProjectItemType } from '../common/constants'; import { EntryType } from 'sqldbproj'; @@ -21,12 +21,12 @@ describe('Project Tree tests', function (): void { const sqlprojUri = vscode.Uri.file(`${root}Fake.sqlproj`); let inputNodes: (FileNode | FolderNode)[] = [ - new FileNode(vscode.Uri.file(`${root}C`), sqlprojUri), - new FileNode(vscode.Uri.file(`${root}D`), sqlprojUri), + new SqlObjectFileNode(vscode.Uri.file(`${root}C`), sqlprojUri), + new SqlObjectFileNode(vscode.Uri.file(`${root}D`), sqlprojUri), new FolderNode(vscode.Uri.file(`${root}Z`), sqlprojUri), new FolderNode(vscode.Uri.file(`${root}X`), sqlprojUri), - new FileNode(vscode.Uri.file(`${root}B`), sqlprojUri), - new FileNode(vscode.Uri.file(`${root}A`), sqlprojUri), + new SqlObjectFileNode(vscode.Uri.file(`${root}B`), sqlprojUri), + new SqlObjectFileNode(vscode.Uri.file(`${root}A`), sqlprojUri), new FolderNode(vscode.Uri.file(`${root}W`), sqlprojUri), new FolderNode(vscode.Uri.file(`${root}Y`), sqlprojUri) ]; @@ -38,10 +38,10 @@ describe('Project Tree tests', function (): void { new FolderNode(vscode.Uri.file(`${root}X`), sqlprojUri), new FolderNode(vscode.Uri.file(`${root}Y`), sqlprojUri), new FolderNode(vscode.Uri.file(`${root}Z`), sqlprojUri), - new FileNode(vscode.Uri.file(`${root}A`), sqlprojUri), - new FileNode(vscode.Uri.file(`${root}B`), sqlprojUri), - new FileNode(vscode.Uri.file(`${root}C`), sqlprojUri), - new FileNode(vscode.Uri.file(`${root}D`), sqlprojUri) + new SqlObjectFileNode(vscode.Uri.file(`${root}A`), sqlprojUri), + new SqlObjectFileNode(vscode.Uri.file(`${root}B`), sqlprojUri), + new SqlObjectFileNode(vscode.Uri.file(`${root}C`), sqlprojUri), + new SqlObjectFileNode(vscode.Uri.file(`${root}D`), sqlprojUri) ]; should(inputNodes.map(n => n.relativeProjectUri.path)).deepEqual(expectedNodes.map(n => n.relativeProjectUri.path)); @@ -86,13 +86,13 @@ describe('Project Tree tests', function (): void { DatabaseProjectItemType.sqlcmdVariablesRoot, DatabaseProjectItemType.folder, DatabaseProjectItemType.folder, - DatabaseProjectItemType.file]); + DatabaseProjectItemType.sqlObjectFile]); should(tree.children.find(x => x.relativeProjectUri.path === '/TestProj/someFolder')?.children.map(y => y.treeItem.contextValue)).deepEqual([ DatabaseProjectItemType.folder, DatabaseProjectItemType.folder, - DatabaseProjectItemType.file, - DatabaseProjectItemType.file]); + DatabaseProjectItemType.sqlObjectFile, + DatabaseProjectItemType.sqlObjectFile]); }); it('Should be able to parse windows relative path as platform safe path', function (): void {