Adding Move, Exclude, and Rename support for folders (#22867)

* Adding exclude folder and base for move folder

* checkpoint

* rename

* Fixing up tests

* Adding exclude test to projController

* Adding tests

* fixing order of service.moveX() calls

* Updating move() order in sqlproj service

* PR feedback

* unskipping

* reskipping test

* Fixing folder move conditional

* updating comments
This commit is contained in:
Benjin Dubishar
2023-04-28 16:05:38 -07:00
committed by GitHub
parent 934d8ff8fa
commit 29ff6ca16c
9 changed files with 175 additions and 161 deletions

View File

@@ -797,31 +797,27 @@ export class ProjectsController {
const node = context.element as BaseProjectTreeItem;
const project = await this.getProjectFromContext(node);
const fileEntry = this.getFileProjectEntry(project, node);
if (fileEntry) {
if (node.entryKey) {
TelemetryReporter.sendActionEvent(TelemetryViews.ProjectTree, TelemetryActions.excludeFromProject);
switch (node.type) {
case constants.DatabaseProjectItemType.sqlObjectScript:
case constants.DatabaseProjectItemType.table:
case constants.DatabaseProjectItemType.externalStreamingJob:
await project.excludeSqlObjectScript(fileEntry.relativePath);
await project.excludeSqlObjectScript(node.entryKey);
break;
case constants.DatabaseProjectItemType.folder:
// TODO: not yet supported in DacFx
//await project.excludeFolder(fileEntry.relativePath);
void vscode.window.showErrorMessage(constants.excludeFolderNotSupported);
await project.excludeFolder(node.entryKey);
break;
case constants.DatabaseProjectItemType.preDeploymentScript:
await project.excludePreDeploymentScript(fileEntry.relativePath);
await project.excludePreDeploymentScript(node.entryKey);
break;
case constants.DatabaseProjectItemType.postDeploymentScript:
await project.excludePostDeploymentScript(fileEntry.relativePath);
await project.excludePostDeploymentScript(node.entryKey);
break;
case constants.DatabaseProjectItemType.noneFile:
case constants.DatabaseProjectItemType.publishProfile:
await project.excludeNoneItem(fileEntry.relativePath);
await project.excludeNoneItem(node.entryKey);
break;
default:
throw new Error(constants.unhandledExcludeType(node.type));
@@ -904,25 +900,21 @@ export class ProjectsController {
public async rename(context: dataworkspace.WorkspaceTreeItem): Promise<void> {
const node = context.element as BaseProjectTreeItem;
const project = await this.getProjectFromContext(node);
const file = this.getFileProjectEntry(project, node);
const baseName = path.basename(node.friendlyName);
let fileExtension: string;
if (utils.isPublishProfile(baseName)) {
fileExtension = constants.publishProfileExtension;
} else {
fileExtension = constants.sqlFileExtension;
}
const originalAbsolutePath = utils.getPlatformSafeFileEntryPath(node.projectFileUri.fsPath);
const originalName = path.basename(node.friendlyName);
const originalExt = path.extname(node.friendlyName);
// need to use quickpick because input box isn't supported in treeviews
// https://github.com/microsoft/vscode/issues/117502 and https://github.com/microsoft/vscode/issues/97190
const newFileName = await vscode.window.showInputBox(
{
title: constants.enterNewName,
value: path.basename(baseName, fileExtension),
value: originalName,
valueSelection: [0, path.basename(originalName, originalExt).length],
ignoreFocusOut: true,
validateInput: async (value) => {
return await this.fileAlreadyExists(value, file?.fsUri.fsPath!) ? constants.fileAlreadyExists(value) : undefined;
validateInput: async (newName) => {
return await this.fileAlreadyExists(newName, originalAbsolutePath) ? constants.fileAlreadyExists(newName) : undefined;
}
});
@@ -930,22 +922,21 @@ export class ProjectsController {
return;
}
const newFilePath = path.join(path.dirname(utils.getPlatformSafeFileEntryPath(file?.relativePath!)), `${newFileName}${fileExtension}`);
const newFilePath = path.join(path.dirname(utils.getPlatformSafeFileEntryPath(node.relativeProjectUri.fsPath!)), newFileName);
const result = await project.move(node, newFilePath);
const renameResult = await project.move(node, newFilePath);
if (renameResult?.success) {
if (result?.success) {
TelemetryReporter.sendActionEvent(TelemetryViews.ProjectTree, TelemetryActions.rename);
} else {
TelemetryReporter.sendErrorEvent2(TelemetryViews.ProjectTree, TelemetryActions.rename);
void vscode.window.showErrorMessage(constants.errorRenamingFile(file?.relativePath!, newFilePath, utils.getErrorMessage(renameResult?.errorMessage)));
void vscode.window.showErrorMessage(constants.errorRenamingFile(node.entryKey!, newFilePath, result?.errorMessage));
}
this.refreshProjectsTree(context);
}
private fileAlreadyExists(newFileName: string, previousFilePath: string): Promise<boolean> {
return utils.exists(path.join(path.dirname(previousFilePath), `${newFileName}.sql`));
return utils.exists(path.join(path.dirname(previousFilePath), newFileName));
}
/**
@@ -1488,21 +1479,6 @@ export class ProjectsController {
//#region Helper methods
private getFileProjectEntry(project: Project, context: BaseProjectTreeItem): FileProjectEntry | undefined {
const fileOrFolder = context as FileNode ? context as FileNode : context as FolderNode;
if (fileOrFolder) {
// use relative path and not tree paths for files and folder
const allFileEntries = project.files.concat(project.preDeployScripts).concat(project.postDeployScripts).concat(project.noneDeployScripts).concat(project.publishProfiles);
// trim trailing slash since folders with and without a trailing slash are allowed in a sqlproj
const trimmedUri = utils.trimChars(utils.getPlatformSafeFileEntryPath(utils.trimUri(fileOrFolder.projectFileUri, fileOrFolder.fileSystemUri)), '/');
return allFileEntries.find(x => utils.trimChars(utils.getPlatformSafeFileEntryPath(x.relativePath), '/') === trimmedUri);
}
const projectRelativeUri = vscode.Uri.file(path.basename(context.projectFileUri.fsPath, constants.sqlprojExtension));
return project.files.find(x => utils.getPlatformSafeFileEntryPath(x.relativePath) === utils.getPlatformSafeFileEntryPath(utils.trimUri(projectRelativeUri, context.relativeProjectUri)));
}
private async getProjectFromContext(context: Project | BaseProjectTreeItem | dataworkspace.WorkspaceTreeItem): Promise<Project> {
if ('element' in context) {
context = context.element;
@@ -1881,23 +1857,22 @@ export class ProjectsController {
//#endregion
/**
* Move a file in the project tree
* Move a file or folder in the project tree
* @param projectUri URI of the project
* @param source
* @param target
*/
public async moveFile(projectUri: vscode.Uri, source: any, target: dataworkspace.WorkspaceTreeItem): Promise<void> {
const sourceFileNode = source as FileNode;
const sourceFileNode = source as FileNode | FolderNode;
const project = await this.getProjectFromContext(sourceFileNode);
// only moving files is supported
if (!sourceFileNode || !(sourceFileNode instanceof FileNode)) {
void vscode.window.showErrorMessage(constants.onlyMoveSqlFilesSupported);
// only moving files and folders are supported
if (!sourceFileNode || !(sourceFileNode instanceof FileNode || sourceFileNode instanceof FolderNode)) {
void vscode.window.showErrorMessage(constants.onlyMoveFilesFoldersSupported);
return;
}
// Moving files to the SQLCMD variables and Database references folders isn't allowed
// TODO: should there be an error displayed if a file attempting to move a file to sqlcmd variables or database references? Or just silently fail and do nothing?
// Moving files/folders to the SQLCMD variables and Database references folders isn't allowed
if (!target.element.fileSystemUri) {
return;
}
@@ -1930,7 +1905,7 @@ export class ProjectsController {
return;
}
// Move the file
// Move the file/folder
const moveResult = await project.move(sourceFileNode, newPath);
if (moveResult?.success) {