mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51: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:
@@ -31,5 +31,4 @@ export class DataWorkspaceExtension implements IExtension {
|
|||||||
validateWorkspace(): Promise<boolean> {
|
validateWorkspace(): Promise<boolean> {
|
||||||
return this.workspaceService.validateWorkspace();
|
return this.workspaceService.validateWorkspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 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 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 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 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 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); }
|
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 { ProjectsController } from './projectController';
|
||||||
import { NetCoreTool } from '../tools/netcoreTool';
|
import { NetCoreTool } from '../tools/netcoreTool';
|
||||||
import { IconPathHelper } from '../common/iconHelper';
|
import { IconPathHelper } from '../common/iconHelper';
|
||||||
import { IProjectProvider, WorkspaceTreeItem } from 'dataworkspace';
|
import { WorkspaceTreeItem } from 'dataworkspace';
|
||||||
import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider';
|
import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +37,7 @@ export default class MainController implements vscode.Disposable {
|
|||||||
public deactivate(): void {
|
public deactivate(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async activate(): Promise<IProjectProvider> {
|
public async activate(): Promise<SqlDatabaseProjectProvider> {
|
||||||
await this.initializeDatabaseProjects();
|
await this.initializeDatabaseProjects();
|
||||||
return new SqlDatabaseProjectProvider(this.projectsController);
|
return new SqlDatabaseProjectProvider(this.projectsController);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -768,7 +768,7 @@ export class ProjectsController {
|
|||||||
|
|
||||||
const project = await Project.openProject(newProjFilePath);
|
const project = await Project.openProject(newProjFilePath);
|
||||||
await this.createProjectFromDatabaseApiCall(model); // Call ExtractAPI in DacFx Service
|
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
|
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.
|
* Generate a flat list of all files and folder under a folder.
|
||||||
*/
|
*/
|
||||||
public async generateList(absolutePath: string): Promise<string[]> {
|
public async generateList(absolutePath: string): Promise<vscode.Uri[]> {
|
||||||
let fileFolderList: string[] = [];
|
let fileFolderList: vscode.Uri[] = [];
|
||||||
|
|
||||||
if (!await utils.exists(absolutePath)) {
|
if (!await utils.exists(absolutePath)) {
|
||||||
if (await utils.exists(absolutePath + constants.sqlFileExtension)) {
|
if (await utils.exists(absolutePath + constants.sqlFileExtension)) {
|
||||||
@@ -831,13 +831,13 @@ export class ProjectsController {
|
|||||||
const stat = await fs.stat(filepath);
|
const stat = await fs.stat(filepath);
|
||||||
|
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
fileFolderList.push(filepath);
|
fileFolderList.push(vscode.Uri.file(filepath));
|
||||||
(await fs
|
(await fs
|
||||||
.readdir(filepath))
|
.readdir(filepath))
|
||||||
.forEach((f: string) => files.push(path.join(filepath, f)));
|
.forEach((f: string) => files.push(path.join(filepath, f)));
|
||||||
}
|
}
|
||||||
else if (stat.isFile()) {
|
else if (stat.isFile()) {
|
||||||
fileFolderList.push(filepath);
|
fileFolderList.push(vscode.Uri.file(filepath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import MainController from './controllers/mainController';
|
import MainController from './controllers/mainController';
|
||||||
import { IProjectProvider } from 'dataworkspace';
|
import { SqlDatabaseProjectProvider } from './projectProvider/projectProvider';
|
||||||
|
|
||||||
let controllers: MainController[] = [];
|
let controllers: MainController[] = [];
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext): Promise<IProjectProvider> {
|
export function activate(context: vscode.ExtensionContext): Promise<SqlDatabaseProjectProvider> {
|
||||||
// Start the main controller
|
// Start the main controller
|
||||||
const mainController = new MainController(context);
|
const mainController = new MainController(context);
|
||||||
controllers.push(mainController);
|
controllers.push(mainController);
|
||||||
|
|||||||
@@ -285,20 +285,20 @@ export class Project {
|
|||||||
* @param contents Contents to be written to the new file
|
* @param contents Contents to be written to the new file
|
||||||
*/
|
*/
|
||||||
public async addScriptItem(relativeFilePath: string, contents?: string, itemType?: string): Promise<FileProjectEntry> {
|
public async addScriptItem(relativeFilePath: string, contents?: string, itemType?: string): Promise<FileProjectEntry> {
|
||||||
// check if file already exists
|
|
||||||
const absoluteFilePath = path.join(this.projectFolderPath, relativeFilePath);
|
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)) {
|
if (contents !== undefined && contents !== '' && await utils.exists(absoluteFilePath)) {
|
||||||
throw new Error(constants.fileAlreadyExists(path.parse(absoluteFilePath).name));
|
throw new Error(constants.fileAlreadyExists(path.parse(absoluteFilePath).name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the file
|
// create the file if contents were passed in
|
||||||
if (contents) {
|
if (contents) {
|
||||||
await fs.mkdir(path.dirname(absoluteFilePath), { recursive: true });
|
await fs.mkdir(path.dirname(absoluteFilePath), { recursive: true });
|
||||||
await fs.writeFile(absoluteFilePath, contents);
|
await fs.writeFile(absoluteFilePath, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that file was successfully created
|
// check that file exists
|
||||||
let exists = await utils.exists(absoluteFilePath);
|
let exists = await utils.exists(absoluteFilePath);
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
throw new Error(constants.noFileExist(absoluteFilePath));
|
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
|
* 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++) {
|
if (!exists) {
|
||||||
let file: string = list[i];
|
throw new Error(constants.fileOrFolderDoesNotExist(file.fsPath));
|
||||||
const relativePath = utils.trimChars(utils.trimUri(Uri.file(this.projectFilePath), Uri.file(file)), '/');
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let file of list) {
|
||||||
|
const relativePath = utils.trimChars(utils.trimUri(Uri.file(this.projectFilePath), file), '/');
|
||||||
|
|
||||||
if (relativePath.length > 0) {
|
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);
|
await this.addScriptItem(relativePath);
|
||||||
}
|
} else if (fileStat.isDirectory()) {
|
||||||
else if (fileStat.isDirectory()) {
|
|
||||||
await this.addFolderItem(relativePath);
|
await this.addFolderItem(relativePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as dataworkspace from 'dataworkspace';
|
import * as dataworkspace from 'dataworkspace';
|
||||||
|
import * as sqldbproj from 'sqldbproj';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import { IconPathHelper } from '../common/iconHelper';
|
import { IconPathHelper } from '../common/iconHelper';
|
||||||
@@ -12,7 +13,7 @@ import { ProjectsController } from '../controllers/projectController';
|
|||||||
import { Project } from '../models/project';
|
import { Project } from '../models/project';
|
||||||
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
||||||
|
|
||||||
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider {
|
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider, sqldbproj.IExtension {
|
||||||
constructor(private projectController: ProjectsController) {
|
constructor(private projectController: ProjectsController) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -74,4 +75,14 @@ export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvide
|
|||||||
|
|
||||||
return vscode.Uri.file(projectFile);
|
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);
|
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||||
const project = await Project.openProject(projFilePath);
|
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);
|
await project.addToProject(list);
|
||||||
|
|
||||||
@@ -137,13 +137,13 @@ describe('Project: sqlproj content operations', function (): void {
|
|||||||
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
|
||||||
const project = await Project.openProject(projFilePath);
|
const project = await Project.openProject(projFilePath);
|
||||||
|
|
||||||
let list: string[] = [];
|
let list: Uri[] = [];
|
||||||
let testFolderPath: string = await testUtils.createDummyFileStructure(true, list, path.dirname(projFilePath));
|
let testFolderPath: string = await testUtils.createDummyFileStructure(true, list, path.dirname(projFilePath));
|
||||||
|
|
||||||
const nonexistentFile = path.join(testFolderPath, 'nonexistentFile.sql');
|
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> {
|
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 should = require('should');
|
||||||
import { AssertionError } from 'assert';
|
import { AssertionError } from 'assert';
|
||||||
import { Project } from '../models/project';
|
import { Project } from '../models/project';
|
||||||
|
import { Uri } from 'vscode';
|
||||||
|
|
||||||
export async function shouldThrowSpecificError(block: Function, expectedMessage: string, details?: string) {
|
export async function shouldThrowSpecificError(block: Function, expectedMessage: string, details?: string) {
|
||||||
let succeeded = false;
|
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 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
|
* @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();
|
testFolderPath = testFolderPath ?? await generateTestFolderPath();
|
||||||
|
|
||||||
let filePath = path.join(testFolderPath, 'file1.sql');
|
let filePath = path.join(testFolderPath, 'file1.sql');
|
||||||
await fs.writeFile(filePath, '');
|
await fs.writeFile(filePath, '');
|
||||||
if (createList) {
|
if (createList) {
|
||||||
list?.push(testFolderPath);
|
list?.push(Uri.file(testFolderPath));
|
||||||
list?.push(filePath);
|
list?.push(Uri.file(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let dirCount = 1; dirCount <= 2; dirCount++) {
|
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 });
|
await fs.mkdir(dirName, { recursive: true });
|
||||||
|
|
||||||
if (createList) {
|
if (createList) {
|
||||||
list?.push(dirName);
|
list?.push(Uri.file(dirName));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let fileCount = 1; fileCount <= 5; fileCount++) {
|
for (let fileCount = 1; fileCount <= 5; fileCount++) {
|
||||||
let fileName = path.join(dirName, `file${fileCount}.sql`);
|
let fileName = path.join(dirName, `file${fileCount}.sql`);
|
||||||
await fs.writeFile(fileName, '');
|
await fs.writeFile(fileName, '');
|
||||||
if (createList) {
|
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 touchFile(filePath);
|
||||||
await fs.writeFile(filePath, '');
|
await fs.writeFile(filePath, '');
|
||||||
if (createList) {
|
if (createList) {
|
||||||
list?.push(filePath);
|
list?.push(Uri.file(filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
return testFolderPath;
|
return testFolderPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createListOfFiles(filePath?: string): Promise<string[]> {
|
export async function createListOfFiles(filePath?: string): Promise<Uri[]> {
|
||||||
let fileFolderList: string[] = [];
|
let fileFolderList: Uri[] = [];
|
||||||
|
|
||||||
await createDummyFileStructure(true, fileFolderList, filePath);
|
await createDummyFileStructure(true, fileFolderList, filePath);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user