Add ability to create publish profile from project context (#23110)

* Ability to add publish profile from project context

* Add/update test + fix Build vs None addition to sqlproj file
This commit is contained in:
Sakshi Sharma
2023-05-15 16:14:07 -07:00
committed by GitHub
parent b56f2ccb60
commit b260edcec3
10 changed files with 64 additions and 6 deletions

View File

@@ -207,6 +207,11 @@
"command": "sqlDatabaseProjects.rename", "command": "sqlDatabaseProjects.rename",
"title": "%sqlDatabaseProjects.rename%", "title": "%sqlDatabaseProjects.rename%",
"category": "%sqlDatabaseProjects.displayName%" "category": "%sqlDatabaseProjects.displayName%"
},
{
"command": "sqlDatabaseProjects.newPublishProfile",
"title": "%sqlDatabaseProjects.newPublishProfile%",
"category": "%sqlDatabaseProjects.displayName%"
} }
], ],
"menus": { "menus": {
@@ -334,6 +339,10 @@
{ {
"command": "sqlDatabaseProjects.rename", "command": "sqlDatabaseProjects.rename",
"when": "false" "when": "false"
},
{
"command": "sqlDatabaseProjects.newPublishProfile",
"when": "false"
} }
], ],
"view/item/context": [ "view/item/context": [
@@ -402,6 +411,11 @@
"when": "view == dataworkspace.views.main && viewItem =~ /^(databaseProject.itemType.project|databaseProject.itemType.legacyProject)$/ || viewItem == databaseProject.itemType.folder", "when": "view == dataworkspace.views.main && viewItem =~ /^(databaseProject.itemType.project|databaseProject.itemType.legacyProject)$/ || viewItem == databaseProject.itemType.folder",
"group": "3_dbProjects_newItem@9" "group": "3_dbProjects_newItem@9"
}, },
{
"command": "sqlDatabaseProjects.newPublishProfile",
"when": "view == dataworkspace.views.main && viewItem =~ /^(databaseProject.itemType.project|databaseProject.itemType.legacyProject)$/ || viewItem == databaseProject.itemType.folder",
"group": "3_dbProjects_newItem@10"
},
{ {
"command": "sqlDatabaseProjects.addDatabaseReference", "command": "sqlDatabaseProjects.addDatabaseReference",
"when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.referencesRoot", "when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.referencesRoot",

View File

@@ -28,6 +28,7 @@
"sqlDatabaseProjects.newItem": "Add Item...", "sqlDatabaseProjects.newItem": "Add Item...",
"sqlDatabaseProjects.addExistingItem": "Add Existing Item...", "sqlDatabaseProjects.addExistingItem": "Add Existing Item...",
"sqlDatabaseProjects.newFolder": "Add Folder", "sqlDatabaseProjects.newFolder": "Add Folder",
"sqlDatabaseProjects.newPublishProfile": "Add Publish Profile",
"sqlDatabaseProjects.addDatabaseReference": "Add Database Reference", "sqlDatabaseProjects.addDatabaseReference": "Add Database Reference",
"sqlDatabaseProjects.addSqlCmdVariable": "Add SQLCMD Variable", "sqlDatabaseProjects.addSqlCmdVariable": "Add SQLCMD Variable",

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetDatabaseName>@@PROJECT_NAME@@</TargetDatabaseName>
<AllowIncompatiblePlatform>True</AllowIncompatiblePlatform>
<DropPermissionsNotInSource>True</DropPermissionsNotInSource>
<DropObjectsNotInSource>True</DropObjectsNotInSource>
<DropRoleMembersNotInSource>True</DropRoleMembersNotInSource>
<IgnoreKeywordCasing>False</IgnoreKeywordCasing>
<IgnoreSemicolonBetweenStatements>False</IgnoreSemicolonBetweenStatements>
<AllowDropBlockingAssemblies>True</AllowDropBlockingAssemblies>
<ProfileVersionNumber>1</ProfileVersionNumber>
</PropertyGroup>
</Project>

View File

@@ -456,6 +456,7 @@ export const externalStreamFriendlyName = localize('externalStream', "External S
export const externalStreamingJobFriendlyName = localize('externalStreamingJobFriendlyName', "External Streaming Job"); export const externalStreamingJobFriendlyName = localize('externalStreamingJobFriendlyName', "External Streaming Job");
export const preDeployScriptFriendlyName = localize('preDeployScriptFriendlyName', "Script.PreDeployment"); export const preDeployScriptFriendlyName = localize('preDeployScriptFriendlyName', "Script.PreDeployment");
export const postDeployScriptFriendlyName = localize('postDeployScriptFriendlyName', "Script.PostDeployment"); export const postDeployScriptFriendlyName = localize('postDeployScriptFriendlyName', "Script.PostDeployment");
export const publishProfileFriendlyName = localize('publishProfileFriendlyName', "Publish Profile");
//#endregion //#endregion

View File

@@ -78,6 +78,7 @@ export default class MainController implements vscode.Disposable {
this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.newItem', async (node: WorkspaceTreeItem) => { return this.projectsController.addItemPromptFromNode(node); })); this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.newItem', async (node: WorkspaceTreeItem) => { return this.projectsController.addItemPromptFromNode(node); }));
this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.addExistingItem', async (node: WorkspaceTreeItem) => { return this.projectsController.addExistingItemPrompt(node); })); this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.addExistingItem', async (node: WorkspaceTreeItem) => { return this.projectsController.addExistingItemPrompt(node); }));
this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.newFolder', async (node: WorkspaceTreeItem) => { return this.projectsController.addFolderPrompt(node); })); this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.newFolder', async (node: WorkspaceTreeItem) => { return this.projectsController.addFolderPrompt(node); }));
this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.newPublishProfile', async (node: WorkspaceTreeItem) => { return this.projectsController.addItemPromptFromNode(node, ItemType.publishProfile); }));
this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.addDatabaseReference', async (node: WorkspaceTreeItem) => { return this.projectsController.addDatabaseReference(node); })); this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.addDatabaseReference', async (node: WorkspaceTreeItem) => { return this.projectsController.addDatabaseReference(node); }));
this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.openContainingFolder', async (node: WorkspaceTreeItem) => { return this.projectsController.openContainingFolder(node); })); this.context.subscriptions.push(vscode.commands.registerCommand('sqlDatabaseProjects.openContainingFolder', async (node: WorkspaceTreeItem) => { return this.projectsController.openContainingFolder(node); }));

View File

@@ -246,6 +246,9 @@ export class ProjectsController {
case ItemType.postDeployScript: case ItemType.postDeployScript:
await project.addPostDeploymentScript(relativePath); await project.addPostDeploymentScript(relativePath);
break; break;
case ItemType.publishProfile:
await project.addNoneItem(relativePath);
break;
default: // a normal SQL object script default: // a normal SQL object script
await project.addSqlObjectScript(relativePath); await project.addSqlObjectScript(relativePath);
break; break;
@@ -729,7 +732,10 @@ export class ProjectsController {
const itemType = templates.get(itemTypeName); const itemType = templates.get(itemTypeName);
const absolutePathToParent = path.join(project.projectFolderPath, relativePath); const absolutePathToParent = path.join(project.projectFolderPath, relativePath);
let itemObjectName = await this.promptForNewObjectName(itemType, project, absolutePathToParent, constants.sqlFileExtension, options?.defaultName); const isItemTypePublishProfile = itemTypeName === constants.publishProfileFriendlyName || itemTypeName === ItemType.publishProfile;
const fileExtension = isItemTypePublishProfile ? constants.publishProfileExtension : constants.sqlFileExtension;
const defaultName = isItemTypePublishProfile ? `${project.projectFileName}_` : options?.defaultName;
let itemObjectName = await this.promptForNewObjectName(itemType, project, absolutePathToParent, fileExtension, defaultName);
itemObjectName = itemObjectName?.trim(); itemObjectName = itemObjectName?.trim();
@@ -737,7 +743,7 @@ export class ProjectsController {
return; // user cancelled return; // user cancelled
} }
const relativeFilePath = path.join(relativePath, itemObjectName + constants.sqlFileExtension); const relativeFilePath = path.join(relativePath, itemObjectName + fileExtension);
const telemetryProps: Record<string, string> = { itemType: itemType.type }; const telemetryProps: Record<string, string> = { itemType: itemType.type };
const telemetryMeasurements: Record<string, number> = {}; const telemetryMeasurements: Record<string, number> = {};
@@ -749,7 +755,7 @@ export class ProjectsController {
} }
try { try {
const absolutePath = await this.addFileToProjectFromTemplate(project, itemType, relativeFilePath, new Map([['OBJECT_NAME', itemObjectName]])); const absolutePath = await this.addFileToProjectFromTemplate(project, itemType, relativeFilePath, new Map([['OBJECT_NAME', itemObjectName], ['PROJECT_NAME', project.projectFileName]]));
TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.addItemFromTree) TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.addItemFromTree)
.withAdditionalProperties(telemetryProps) .withAdditionalProperties(telemetryProps)

View File

@@ -104,7 +104,8 @@ declare module 'sqldbproj' {
externalStreamingJob = 'externalStreamingJob', externalStreamingJob = 'externalStreamingJob',
folder = 'folder', folder = 'folder',
preDeployScript = 'preDeployScript', preDeployScript = 'preDeployScript',
postDeployScript = 'postDeployScript' postDeployScript = 'postDeployScript',
publishProfile = 'publishProfile'
} }
/** /**
@@ -152,6 +153,12 @@ declare module 'sqldbproj' {
*/ */
addPostDeploymentScript(relativePath: string): Promise<void>; addPostDeploymentScript(relativePath: string): Promise<void>;
/**
* Adds a none item that is not included in "Build"
* @param relativePath
*/
addNoneItem(relativePath: string): Promise<void>;
/** /**
* Add a SQL object script that will be included in the schema * Add a SQL object script that will be included in the schema
* @param relativePath * @param relativePath

View File

@@ -48,7 +48,8 @@ export async function loadTemplates(templateFolderPath: string) {
loadObjectTypeInfo(ItemType.dataSource, constants.dataSourceFriendlyName, templateFolderPath, 'newTsqlDataSourceTemplate.sql'), loadObjectTypeInfo(ItemType.dataSource, constants.dataSourceFriendlyName, templateFolderPath, 'newTsqlDataSourceTemplate.sql'),
loadObjectTypeInfo(ItemType.fileFormat, constants.fileFormatFriendlyName, templateFolderPath, 'newTsqlFileFormatTemplate.sql'), loadObjectTypeInfo(ItemType.fileFormat, constants.fileFormatFriendlyName, templateFolderPath, 'newTsqlFileFormatTemplate.sql'),
loadObjectTypeInfo(ItemType.externalStream, constants.externalStreamFriendlyName, templateFolderPath, 'newTsqlExternalStreamTemplate.sql'), loadObjectTypeInfo(ItemType.externalStream, constants.externalStreamFriendlyName, templateFolderPath, 'newTsqlExternalStreamTemplate.sql'),
loadObjectTypeInfo(ItemType.externalStreamingJob, constants.externalStreamingJobFriendlyName, templateFolderPath, 'newTsqlExternalStreamingJobTemplate.sql') loadObjectTypeInfo(ItemType.externalStreamingJob, constants.externalStreamingJobFriendlyName, templateFolderPath, 'newTsqlExternalStreamingJobTemplate.sql'),
loadObjectTypeInfo(ItemType.publishProfile, constants.publishProfileFriendlyName, templateFolderPath, 'newPublishProfileTemplate.publish.xml')
]); ]);
for (const scriptType of scriptTypes) { for (const scriptType of scriptTypes) {

View File

@@ -429,6 +429,19 @@ describe('ProjectsController', function (): void {
should(project.postDeployScripts.length).equal(1, 'Post deploy script should be successfully added'); should(project.postDeployScripts.length).equal(1, 'Post deploy script should be successfully added');
}); });
it('Should be able to add publish profile', async function (): Promise<void> {
const publishProfileName = 'profile.publish.xml';
const projController = new ProjectsController(testContext.outputChannel);
const project = await testUtils.createTestProject(this.test, baselines.newProjectFileBaseline);
sinon.stub(vscode.window, 'showInputBox').resolves(publishProfileName);
sinon.stub(utils, 'sanitizeStringForFilename').returns(publishProfileName);
should(project.publishProfiles.length).equal(0, 'There should be no publish profiles');
await projController.addItemPrompt(project, '', { itemType: ItemType.publishProfile });
should(project.publishProfiles.length).equal(1, 'Publish profile should be successfully added.');
});
it('Should change target platform', async function (): Promise<void> { it('Should change target platform', async function (): Promise<void> {
sinon.stub(vscode.window, 'showQuickPick').resolves({ label: SqlTargetPlatform.sqlAzure }); sinon.stub(vscode.window, 'showQuickPick').resolves({ label: SqlTargetPlatform.sqlAzure });

View File

@@ -23,7 +23,7 @@ describe('Templates: loading templates from disk', function (): void {
// check expected counts // check expected counts
const numScriptObjectTypes = 10; const numScriptObjectTypes = 11;
should(templates.projectScriptTypes().length).equal(numScriptObjectTypes); should(templates.projectScriptTypes().length).equal(numScriptObjectTypes);
should(Object.keys(templates.projectScriptTypes()).length).equal(numScriptObjectTypes); should(Object.keys(templates.projectScriptTypes()).length).equal(numScriptObjectTypes);