Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,28 @@ export namespace MoveNoneItemRequest {

//#endregion

//#region RefactorLog functions

export namespace GetRefactorLogItemsRequest {
export const type = new RequestType<mssql.SqlProjectParams, mssql.GetScriptsResult, void>(
"sqlProjects/getRefactorLogItems",
);
}

export namespace AddRefactorLogItemRequest {
export const type = new RequestType<mssql.SqlProjectScriptParams, mssql.ResultStatus, void>(
"sqlProjects/addRefactorLogItem",
);
}

export namespace DeleteRefactorLogItemRequest {
export const type = new RequestType<mssql.SqlProjectScriptParams, mssql.ResultStatus, void>(
"sqlProjects/deleteRefactorLogItem",
);
}

//#endregion

//#endregion

//#region SQLCMD variable functions
Expand Down
38 changes: 38 additions & 0 deletions extensions/mssql/src/services/sqlProjectsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,4 +713,42 @@ export class SqlProjectsService implements mssql.ISqlProjectsService {
};
return this._client.sendRequest(contracts.MoveNoneItemRequest.type, params);
}

/**
* Get all RefactorLog items in a project
* @param projectUri Absolute path of the project, including .sqlproj
*/
public async getRefactorLogItems(projectUri: string): Promise<mssql.GetScriptsResult> {
const params: mssql.SqlProjectParams = { projectUri: projectUri };
return this._client.sendRequest(contracts.GetRefactorLogItemsRequest.type, params);
}

/**
* Add a RefactorLog item to a project
* @param projectUri Absolute path of the project, including .sqlproj
* @param path Path of the .refactorlog file, relative to the .sqlproj
*/
public async addRefactorLogItem(projectUri: string, path: string): Promise<mssql.ResultStatus> {
const params: mssql.SqlProjectScriptParams = {
projectUri: projectUri,
path: path,
};
return this._client.sendRequest(contracts.AddRefactorLogItemRequest.type, params);
}

/**
* Delete a RefactorLog item from a project
* @param projectUri Absolute path of the project, including .sqlproj
* @param path Path of the .refactorlog file, relative to the .sqlproj
*/
public async deleteRefactorLogItem(
projectUri: string,
path: string,
): Promise<mssql.ResultStatus> {
const params: mssql.SqlProjectScriptParams = {
projectUri: projectUri,
path: path,
};
return this._client.sendRequest(contracts.DeleteRefactorLogItemRequest.type, params);
}
}
107 changes: 107 additions & 0 deletions extensions/mssql/test/unit/sqlProjectsService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as sinon from "sinon";
import * as chai from "chai";
import { expect } from "chai";
import sinonChai from "sinon-chai";
import SqlToolsServiceClient from "../../src/languageservice/serviceclient";
import { SqlProjectsService } from "../../src/services/sqlProjectsService";
import * as contracts from "../../src/models/contracts/sqlProjects/sqlProjectsContracts";
import { GetScriptsResult, ResultStatus } from "vscode-mssql";

chai.use(sinonChai);

suite("SqlProjectsService - RefactorLog methods", () => {
const PROJECT_URI = "/path/to/TestProject.sqlproj";
const REFACTORLOG_PATH = "TestProject.refactorlog";

let sandbox: sinon.SinonSandbox;
let clientStub: sinon.SinonStubbedInstance<SqlToolsServiceClient>;
let service: SqlProjectsService;

setup(() => {
sandbox = sinon.createSandbox();
clientStub = sandbox.createStubInstance(SqlToolsServiceClient);
service = new SqlProjectsService(clientStub as unknown as SqlToolsServiceClient);
});

teardown(() => {
sandbox.restore();
});

test("getRefactorLogItems sends GetRefactorLogItemsRequest with correct params", async () => {
const expectedResult: GetScriptsResult = {
success: true,
errorMessage: "",
scripts: [REFACTORLOG_PATH],
};
clientStub.sendRequest.resolves(expectedResult);

const result = await service.getRefactorLogItems(PROJECT_URI);

expect(clientStub.sendRequest).to.have.been.calledOnceWith(
contracts.GetRefactorLogItemsRequest.type,
{ projectUri: PROJECT_URI },
);
expect(result).to.deep.equal(expectedResult);
expect(result.scripts).to.deep.equal([REFACTORLOG_PATH]);
});

test("addRefactorLogItem sends AddRefactorLogItemRequest with correct params", async () => {
const expectedResult: ResultStatus = { success: true, errorMessage: "" };
clientStub.sendRequest.resolves(expectedResult);

const result = await service.addRefactorLogItem(PROJECT_URI, REFACTORLOG_PATH);

expect(clientStub.sendRequest).to.have.been.calledOnceWith(
contracts.AddRefactorLogItemRequest.type,
{ projectUri: PROJECT_URI, path: REFACTORLOG_PATH },
);
expect(result).to.deep.equal(expectedResult);
expect(result.success).to.be.true;
});

test("deleteRefactorLogItem sends DeleteRefactorLogItemRequest with correct params", async () => {
const expectedResult: ResultStatus = { success: true, errorMessage: "" };
clientStub.sendRequest.resolves(expectedResult);

const result = await service.deleteRefactorLogItem(PROJECT_URI, REFACTORLOG_PATH);

expect(clientStub.sendRequest).to.have.been.calledOnceWith(
contracts.DeleteRefactorLogItemRequest.type,
{ projectUri: PROJECT_URI, path: REFACTORLOG_PATH },
);
expect(result).to.deep.equal(expectedResult);
expect(result.success).to.be.true;
});

test("getRefactorLogItems returns empty scripts array when project has no RefactorLog items", async () => {
const expectedResult: GetScriptsResult = {
success: true,
errorMessage: "",
scripts: [],
};
clientStub.sendRequest.resolves(expectedResult);

const result = await service.getRefactorLogItems(PROJECT_URI);

expect(result.success).to.be.true;
expect(result.scripts).to.deep.equal([]);
});

test("addRefactorLogItem propagates failure from service", async () => {
const expectedResult: ResultStatus = {
success: false,
errorMessage: "File not found: TestProject.refactorlog",
};
clientStub.sendRequest.resolves(expectedResult);

const result = await service.addRefactorLogItem(PROJECT_URI, REFACTORLOG_PATH);

expect(result.success).to.be.false;
expect(result.errorMessage).to.include("File not found");
});
});
20 changes: 20 additions & 0 deletions extensions/mssql/typings/vscode-mssql.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,26 @@ declare module "vscode-mssql" {
path: string,
destinationPath: string,
): Promise<ResultStatus>;

/**
* Get all RefactorLog items in a project
* @param projectUri Absolute path of the project, including .sqlproj
*/
getRefactorLogItems(projectUri: string): Promise<GetScriptsResult>;

/**
* Add a RefactorLog item to a project
* @param projectUri Absolute path of the project, including .sqlproj
* @param path Path of the .refactorlog file, relative to the .sqlproj
*/
addRefactorLogItem(projectUri: string, path: string): Promise<ResultStatus>;

/**
* Delete a RefactorLog item from a project
* @param projectUri Absolute path of the project, including .sqlproj
* @param path Path of the .refactorlog file, relative to the .sqlproj
*/
deleteRefactorLogItem(projectUri: string, path: string): Promise<ResultStatus>;
}

/**
Expand Down
1 change: 1 addition & 0 deletions extensions/sql-database-projects/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@
export const dataSourceDropdownTitle = l10n.t("Data source");
export const noDataSourcesText = l10n.t("No data sources in this project");
export const loadProfilePlaceholderText = l10n.t("Load profile...");
export const profileReadError = (err: any) =>

Check warning on line 210 in extensions/sql-database-projects/src/common/constants.ts

View workflow job for this annotation

GitHub Actions / build-and-test

Unexpected any. Specify a different type
l10n.t("Error loading the publish profile. {0}", utils.getErrorMessage(err));
export const sqlCmdVariables = l10n.t("SQLCMD Variables");
export const sqlCmdVariableColumn = l10n.t("Name");
Expand Down Expand Up @@ -533,7 +533,7 @@
export function circularProjectReference(project1: string, project2: string) {
return l10n.t("Circular reference from project {0} to project {1}", project1, project2);
}
export function errorFindingBuildFilesLocation(err: any) {

Check warning on line 536 in extensions/sql-database-projects/src/common/constants.ts

View workflow job for this annotation

GitHub Actions / build-and-test

Unexpected any. Specify a different type
return l10n.t("Error finding build files location: {0}", utils.getErrorMessage(err));
}
export function projBuildFailed(errorMessage: string) {
Expand Down Expand Up @@ -780,6 +780,7 @@
noneFile = "databaseProject.itemType.file.noneFile",
sqlObjectScript = "databaseProject.itemType.file.sqlObjectScript",
publishProfile = "databaseProject.itemType.file.publishProfile",
refactorLogFile = "databaseProject.itemType.file.refactorLogFile",
}

//#endregion
Expand Down
54 changes: 54 additions & 0 deletions extensions/sql-database-projects/src/models/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export class Project implements ISqlProject {
private _preDeployScripts: FileProjectEntry[] = [];
private _postDeployScripts: FileProjectEntry[] = [];
private _noneDeployScripts: FileProjectEntry[] = [];
private _refactorLogItems: FileProjectEntry[] = [];
private _sqlProjStyle: ProjectType;
private _isCrossPlatformCompatible: boolean = false;
private _outputPath: string = "";
Expand Down Expand Up @@ -138,6 +139,10 @@ export class Project implements ISqlProject {
return this._noneDeployScripts;
}

public get refactorLogItems(): FileProjectEntry[] {
return this._refactorLogItems;
}

public get sqlProjStyle(): ProjectType {
return this._sqlProjStyle;
}
Expand Down Expand Up @@ -347,6 +352,7 @@ export class Project implements ISqlProject {
await this.readPostDeployScripts(true);

await this.readNoneItems(); // also populates list of publish profiles, determined by file extension
await this.readRefactorLogItems();

await this.readSqlObjectScripts(); // get SQL object scripts
await this.readFolders(); // get folders
Expand Down Expand Up @@ -556,6 +562,23 @@ export class Project implements ISqlProject {
}
}

private async readRefactorLogItems(): Promise<void> {
const result: GetScriptsResult = await (
this.sqlProjService as vscodeMssql.ISqlProjectsService
).getRefactorLogItems(this.projectFilePath);
utils.throwIfFailed(result);

this._refactorLogItems = [];

if (result.scripts?.length > 0) {
for (var scriptPath of result.scripts) {
this._refactorLogItems.push(
this.createFileProjectEntry(scriptPath, EntryType.File),
);
}
}
}

private async readDatabaseReferences(): Promise<void> {
this._databaseReferences = [];
const databaseReferencesResult = await this.sqlProjService.getDatabaseReferences(
Expand Down Expand Up @@ -639,6 +662,7 @@ export class Project implements ISqlProject {
this._preDeployScripts = [];
this._postDeployScripts = [];
this._noneDeployScripts = [];
this._refactorLogItems = [];
this._outputPath = "";
this._configuration = Configuration.Debug;
this._publishProfiles = [];
Expand Down Expand Up @@ -740,6 +764,7 @@ export class Project implements ISqlProject {
await this.readPreDeployScripts();
await this.readPostDeployScripts();
await this.readNoneItems();
await this.readRefactorLogItems();
await this.readFolders();
}

Expand All @@ -754,6 +779,7 @@ export class Project implements ISqlProject {
await this.readPreDeployScripts();
await this.readPostDeployScripts();
await this.readNoneItems();
await this.readRefactorLogItems();
await this.readFolders();
}

Expand All @@ -772,6 +798,7 @@ export class Project implements ISqlProject {
await this.readPreDeployScripts();
await this.readPostDeployScripts();
await this.readNoneItems();
await this.readRefactorLogItems();
await this.readFolders();
}

Expand Down Expand Up @@ -845,6 +872,7 @@ export class Project implements ISqlProject {

await this.readPreDeployScripts();
await this.readNoneItems();
await this.readRefactorLogItems();
await this.readFolders();
}

Expand Down Expand Up @@ -889,6 +917,7 @@ export class Project implements ISqlProject {

await this.readPostDeployScripts();
await this.readNoneItems();
await this.readRefactorLogItems();
await this.readFolders();
}

Expand Down Expand Up @@ -945,6 +974,30 @@ export class Project implements ISqlProject {
await this.readFolders();
}

//#region RefactorLog items

public async addRefactorLogItem(relativePath: string): Promise<void> {
const result = await (
this.sqlProjService as vscodeMssql.ISqlProjectsService
).addRefactorLogItem(this.projectFilePath, relativePath);
utils.throwIfFailed(result);

await this.readRefactorLogItems();
await this.readFolders();
}

public async deleteRefactorLogItem(relativePath: string): Promise<void> {
const result = await (
this.sqlProjService as vscodeMssql.ISqlProjectsService
).deleteRefactorLogItem(this.projectFilePath, relativePath);
utils.throwIfFailed(result);

await this.readRefactorLogItems();
await this.readFolders();
}

//#endregion

//#endregion

//#endregion
Expand Down Expand Up @@ -1018,6 +1071,7 @@ export class Project implements ISqlProject {
normalizedRelativeFilePath,
);
await this.readNoneItems();
await this.readRefactorLogItems();
}

utils.throwIfFailed(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,19 @@ export class NoneNode extends FileNode {
}
}

export class RefactorLogNode extends FileNode {
public override get treeItem(): vscode.TreeItem {
const treeItem = super.treeItem;
treeItem.contextValue = this.type;

return treeItem;
}

public get type(): DatabaseProjectItemType {
return DatabaseProjectItemType.refactorLogFile;
}
}

export class PublishProfileNode extends FileNode {
public override get treeItem(): vscode.TreeItem {
const treeItem = super.treeItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ export class ProjectRootTreeItem extends BaseProjectTreeItem {
this.addNode(newNode, noneEntry);
}

// refactor log items
for (const refactorLogEntry of this.project.refactorLogItems) {
const newNode = new fileTree.RefactorLogNode(
refactorLogEntry.fsUri,
this.projectFileUri,
refactorLogEntry.relativePath,
);
this.addNode(newNode, refactorLogEntry);
}

// publish profiles
for (const publishProfile of this.project.publishProfiles) {
const newNode = new fileTree.PublishProfileNode(
Expand Down
Loading
Loading