mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 09:35:36 -05:00
Expose adding files and folders in sql database projects (#14391)
* expose addToProject in dataworkspace.d.ts * remove changes in data workspace extension * add sqldbproj.d.ts * change list to be Uris instead of strings * don't add files/folders if any don't exist * fix test on windows
This commit is contained in:
@@ -170,6 +170,7 @@ export const externalStreamingJobValidationPassed = localize('externalStreamingJ
|
||||
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); }
|
||||
export function fileOrFolderDoesNotExist(name: string) { return localize('fileOrFolderDoesNotExist', "File or directory '{0}' doesn't exist", name); }
|
||||
export function cannotResolvePath(path: string) { return localize('cannotResolvePath', "Cannot resolve path {0}", path); }
|
||||
export function fileAlreadyExists(filename: string) { return localize('fileAlreadyExists', "A file with the name '{0}' already exists on disk at this location. Please choose another name.", filename); }
|
||||
export function folderAlreadyExists(filename: string) { return localize('folderAlreadyExists', "A folder with the name '{0}' already exists on disk at this location. Please choose another name.", filename); }
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as path from 'path';
|
||||
import { ProjectsController } from './projectController';
|
||||
import { NetCoreTool } from '../tools/netcoreTool';
|
||||
import { IconPathHelper } from '../common/iconHelper';
|
||||
import { IProjectProvider, WorkspaceTreeItem } from 'dataworkspace';
|
||||
import { WorkspaceTreeItem } from 'dataworkspace';
|
||||
import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider';
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ export default class MainController implements vscode.Disposable {
|
||||
public deactivate(): void {
|
||||
}
|
||||
|
||||
public async activate(): Promise<IProjectProvider> {
|
||||
public async activate(): Promise<SqlDatabaseProjectProvider> {
|
||||
await this.initializeDatabaseProjects();
|
||||
return new SqlDatabaseProjectProvider(this.projectsController);
|
||||
}
|
||||
|
||||
@@ -768,7 +768,7 @@ export class ProjectsController {
|
||||
|
||||
const project = await Project.openProject(newProjFilePath);
|
||||
await this.createProjectFromDatabaseApiCall(model); // Call ExtractAPI in DacFx Service
|
||||
let fileFolderList: string[] = model.extractTarget === mssql.ExtractTarget.file ? [model.filePath] : await this.generateList(model.filePath); // Create a list of all the files and directories to be added to project
|
||||
let fileFolderList: vscode.Uri[] = model.extractTarget === mssql.ExtractTarget.file ? [vscode.Uri.file(model.filePath)] : await this.generateList(model.filePath); // Create a list of all the files and directories to be added to project
|
||||
|
||||
await project.addToProject(fileFolderList); // Add generated file structure to the project
|
||||
|
||||
@@ -811,8 +811,8 @@ export class ProjectsController {
|
||||
/**
|
||||
* Generate a flat list of all files and folder under a folder.
|
||||
*/
|
||||
public async generateList(absolutePath: string): Promise<string[]> {
|
||||
let fileFolderList: string[] = [];
|
||||
public async generateList(absolutePath: string): Promise<vscode.Uri[]> {
|
||||
let fileFolderList: vscode.Uri[] = [];
|
||||
|
||||
if (!await utils.exists(absolutePath)) {
|
||||
if (await utils.exists(absolutePath + constants.sqlFileExtension)) {
|
||||
@@ -831,13 +831,13 @@ export class ProjectsController {
|
||||
const stat = await fs.stat(filepath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
fileFolderList.push(filepath);
|
||||
fileFolderList.push(vscode.Uri.file(filepath));
|
||||
(await fs
|
||||
.readdir(filepath))
|
||||
.forEach((f: string) => files.push(path.join(filepath, f)));
|
||||
}
|
||||
else if (stat.isFile()) {
|
||||
fileFolderList.push(filepath);
|
||||
fileFolderList.push(vscode.Uri.file(filepath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import MainController from './controllers/mainController';
|
||||
import { IProjectProvider } from 'dataworkspace';
|
||||
import { SqlDatabaseProjectProvider } from './projectProvider/projectProvider';
|
||||
|
||||
let controllers: MainController[] = [];
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): Promise<IProjectProvider> {
|
||||
export function activate(context: vscode.ExtensionContext): Promise<SqlDatabaseProjectProvider> {
|
||||
// Start the main controller
|
||||
const mainController = new MainController(context);
|
||||
controllers.push(mainController);
|
||||
|
||||
@@ -285,20 +285,20 @@ export class Project {
|
||||
* @param contents Contents to be written to the new file
|
||||
*/
|
||||
public async addScriptItem(relativeFilePath: string, contents?: string, itemType?: string): Promise<FileProjectEntry> {
|
||||
// check if file already exists
|
||||
const absoluteFilePath = path.join(this.projectFolderPath, relativeFilePath);
|
||||
|
||||
// check if file already exists if content was passed to write to a new file
|
||||
if (contents !== undefined && contents !== '' && await utils.exists(absoluteFilePath)) {
|
||||
throw new Error(constants.fileAlreadyExists(path.parse(absoluteFilePath).name));
|
||||
}
|
||||
|
||||
// create the file
|
||||
// create the file if contents were passed in
|
||||
if (contents) {
|
||||
await fs.mkdir(path.dirname(absoluteFilePath), { recursive: true });
|
||||
await fs.writeFile(absoluteFilePath, contents);
|
||||
}
|
||||
|
||||
// check that file was successfully created
|
||||
// check that file exists
|
||||
let exists = await utils.exists(absoluteFilePath);
|
||||
if (!exists) {
|
||||
throw new Error(constants.noFileExist(absoluteFilePath));
|
||||
@@ -928,21 +928,27 @@ export class Project {
|
||||
|
||||
/**
|
||||
* Adds the list of sql files and directories to the project, and saves the project file
|
||||
* @param absolutePath Absolute path of the folder
|
||||
* @param list list of files and folder Uris. Files and folders must already exist. No files or folders will be added if any do not exist.
|
||||
*/
|
||||
public async addToProject(list: string[]): Promise<void> {
|
||||
public async addToProject(list: Uri[]): Promise<void> {
|
||||
// verify all files/folders exist. If not all exist, none will be added
|
||||
for (let file of list) {
|
||||
const exists = await utils.exists(file.fsPath);
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let file: string = list[i];
|
||||
const relativePath = utils.trimChars(utils.trimUri(Uri.file(this.projectFilePath), Uri.file(file)), '/');
|
||||
if (!exists) {
|
||||
throw new Error(constants.fileOrFolderDoesNotExist(file.fsPath));
|
||||
}
|
||||
}
|
||||
|
||||
for (let file of list) {
|
||||
const relativePath = utils.trimChars(utils.trimUri(Uri.file(this.projectFilePath), file), '/');
|
||||
|
||||
if (relativePath.length > 0) {
|
||||
let fileStat = await fs.stat(file);
|
||||
const fileStat = await fs.stat(file.fsPath);
|
||||
|
||||
if (fileStat.isFile() && file.toLowerCase().endsWith(constants.sqlFileExtension)) {
|
||||
if (fileStat.isFile() && file.fsPath.toLowerCase().endsWith(constants.sqlFileExtension)) {
|
||||
await this.addScriptItem(relativePath);
|
||||
}
|
||||
else if (fileStat.isDirectory()) {
|
||||
} else if (fileStat.isDirectory()) {
|
||||
await this.addFolderItem(relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dataworkspace from 'dataworkspace';
|
||||
import * as sqldbproj from 'sqldbproj';
|
||||
import * as vscode from 'vscode';
|
||||
import * as constants from '../common/constants';
|
||||
import { IconPathHelper } from '../common/iconHelper';
|
||||
@@ -12,7 +13,7 @@ import { ProjectsController } from '../controllers/projectController';
|
||||
import { Project } from '../models/project';
|
||||
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
||||
|
||||
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider {
|
||||
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider, sqldbproj.IExtension {
|
||||
constructor(private projectController: ProjectsController) {
|
||||
|
||||
}
|
||||
@@ -74,4 +75,14 @@ export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvide
|
||||
|
||||
return vscode.Uri.file(projectFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the list of files and directories to the project, and saves the project file
|
||||
* @param projectFile The Uri of the project file
|
||||
* @param list list of uris of files and folders to add. Files and folders must already exist. Files and folders must already exist. No files or folders will be added if any do not exist.
|
||||
*/
|
||||
async addToProject(projectFile: vscode.Uri, list: vscode.Uri[]): Promise<void> {
|
||||
const project = await Project.openProject(projectFile.fsPath);
|
||||
await project.addToProject(list);
|
||||
}
|
||||
}
|
||||
|
||||
32
extensions/sql-database-projects/src/sqldbproj.d.ts
vendored
Normal file
32
extensions/sql-database-projects/src/sqldbproj.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'sqldbproj' {
|
||||
import * as vscode from 'vscode';
|
||||
export const enum extension {
|
||||
name = 'Microsoft.sql-database-projects'
|
||||
}
|
||||
|
||||
/**
|
||||
* sql database projects extension
|
||||
*/
|
||||
export interface IExtension {
|
||||
/**
|
||||
* Create a project
|
||||
* @param name name of the project
|
||||
* @param location the parent directory
|
||||
* @param projectTypeId the ID of the project/template
|
||||
* @returns Uri of the newly created project file
|
||||
*/
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string): Promise<vscode.Uri>;
|
||||
|
||||
/**
|
||||
* Adds the list of files and directories to the project, and saves the project file
|
||||
* @param projectFile The Uri of the project file
|
||||
* @param list list of uris of files and folders to add. Files and folders must already exist. No files or folders will be added if any do not exist.
|
||||
*/
|
||||
addToProject(projectFile: vscode.Uri, list: vscode.Uri[]): Promise<void>;
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
let list: string[] = await testUtils.createListOfFiles(path.dirname(projFilePath));
|
||||
let list: Uri[] = await testUtils.createListOfFiles(path.dirname(projFilePath));
|
||||
|
||||
await project.addToProject(list);
|
||||
|
||||
@@ -137,13 +137,13 @@ describe('Project: sqlproj content operations', function (): void {
|
||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
let list: string[] = [];
|
||||
let list: Uri[] = [];
|
||||
let testFolderPath: string = await testUtils.createDummyFileStructure(true, list, path.dirname(projFilePath));
|
||||
|
||||
const nonexistentFile = path.join(testFolderPath, 'nonexistentFile.sql');
|
||||
list.push(nonexistentFile);
|
||||
list.push(Uri.file(nonexistentFile));
|
||||
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.addToProject(list), `ENOENT: no such file or directory, stat \'${nonexistentFile}\'`);
|
||||
await testUtils.shouldThrowSpecificError(async () => await project.addToProject(list), constants.fileOrFolderDoesNotExist(Uri.file(nonexistentFile).fsPath));
|
||||
});
|
||||
|
||||
it('Should choose correct master dacpac', async function (): Promise<void> {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { promises as fs } from 'fs';
|
||||
import should = require('should');
|
||||
import { AssertionError } from 'assert';
|
||||
import { Project } from '../models/project';
|
||||
import { Uri } from 'vscode';
|
||||
|
||||
export async function shouldThrowSpecificError(block: Function, expectedMessage: string, details?: string) {
|
||||
let succeeded = false;
|
||||
@@ -77,14 +78,14 @@ export async function createTestFile(contents: string, fileName: string, folderP
|
||||
* @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 createDummyFileStructure(createList?: boolean, list?: string[], testFolderPath?: string): Promise<string> {
|
||||
export async function createDummyFileStructure(createList?: boolean, list?: Uri[], testFolderPath?: string): Promise<string> {
|
||||
testFolderPath = testFolderPath ?? await generateTestFolderPath();
|
||||
|
||||
let filePath = path.join(testFolderPath, 'file1.sql');
|
||||
await fs.writeFile(filePath, '');
|
||||
if (createList) {
|
||||
list?.push(testFolderPath);
|
||||
list?.push(filePath);
|
||||
list?.push(Uri.file(testFolderPath));
|
||||
list?.push(Uri.file(filePath));
|
||||
}
|
||||
|
||||
for (let dirCount = 1; dirCount <= 2; dirCount++) {
|
||||
@@ -92,14 +93,14 @@ export async function createDummyFileStructure(createList?: boolean, list?: stri
|
||||
await fs.mkdir(dirName, { recursive: true });
|
||||
|
||||
if (createList) {
|
||||
list?.push(dirName);
|
||||
list?.push(Uri.file(dirName));
|
||||
}
|
||||
|
||||
for (let fileCount = 1; fileCount <= 5; fileCount++) {
|
||||
let fileName = path.join(dirName, `file${fileCount}.sql`);
|
||||
await fs.writeFile(fileName, '');
|
||||
if (createList) {
|
||||
list?.push(fileName);
|
||||
list?.push(Uri.file(fileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,14 +109,14 @@ export async function createDummyFileStructure(createList?: boolean, list?: stri
|
||||
//await touchFile(filePath);
|
||||
await fs.writeFile(filePath, '');
|
||||
if (createList) {
|
||||
list?.push(filePath);
|
||||
list?.push(Uri.file(filePath));
|
||||
}
|
||||
|
||||
return testFolderPath;
|
||||
}
|
||||
|
||||
export async function createListOfFiles(filePath?: string): Promise<string[]> {
|
||||
let fileFolderList: string[] = [];
|
||||
export async function createListOfFiles(filePath?: string): Promise<Uri[]> {
|
||||
let fileFolderList: Uri[] = [];
|
||||
|
||||
await createDummyFileStructure(true, fileFolderList, filePath);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user