diff --git a/extensions/dacpac/src/test/testDacFxService.ts b/extensions/dacpac/src/test/testDacFxService.ts index b08a727557..9ebe4bbc0b 100644 --- a/extensions/dacpac/src/test/testDacFxService.ts +++ b/extensions/dacpac/src/test/testDacFxService.ts @@ -39,11 +39,11 @@ export class DacFxTestService implements mssql.IDacFxService { this.dacfxResult.operationId = importOperationId; return Promise.resolve(this.dacfxResult); } - deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record): Promise { + deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map): Promise { this.dacfxResult.operationId = deployOperationId; return Promise.resolve(this.dacfxResult); } - generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record): Promise { + generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map): Promise { this.dacfxResult.operationId = generateScript; return Promise.resolve(this.dacfxResult); } @@ -90,7 +90,7 @@ export class DacFxTestService implements mssql.IDacFxService { return Promise.resolve({ containsCreateTableStatement: true }); } - savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Record): Thenable { + savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map): Thenable { return Promise.resolve(this.dacfxResult); } } diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index d859695106..609e2ae5dc 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -515,7 +515,7 @@ export interface DeployParams { packageFilePath: string; databaseName: string; upgradeExisting: boolean; - sqlCommandVariableValues?: Record; + sqlCommandVariableValues?: Map; deploymentOptions?: mssql.DeploymentOptions; ownerUri: string; taskExecutionMode: TaskExecutionMode; @@ -524,7 +524,7 @@ export interface DeployParams { export interface GenerateDeployScriptParams { packageFilePath: string; databaseName: string; - sqlCommandVariableValues?: Record; + sqlCommandVariableValues?: Map; deploymentOptions?: mssql.DeploymentOptions ownerUri: string; taskExecutionMode: TaskExecutionMode; @@ -555,7 +555,7 @@ export interface SavePublishProfileParams { profilePath: string; databaseName: string; connectionString: string; - sqlCommandVariableValues?: Record; + sqlCommandVariableValues?: Map; deploymentOptions?: mssql.DeploymentOptions; } diff --git a/extensions/mssql/src/dacfx/dacFxService.ts b/extensions/mssql/src/dacfx/dacFxService.ts index 6a3a0855f7..1e6ba0050e 100644 --- a/extensions/mssql/src/dacfx/dacFxService.ts +++ b/extensions/mssql/src/dacfx/dacFxService.ts @@ -54,12 +54,12 @@ export class DacFxService extends BaseService implements mssql.IDacFxService { return this.runWithErrorHandling(contracts.ExtractRequest.type, params); } - public async deployDacpac(packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: mssql.DeploymentOptions): Promise { + public async deployDacpac(packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map, deploymentOptions?: mssql.DeploymentOptions): Promise { const params: contracts.DeployParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, upgradeExisting: upgradeExisting, sqlCommandVariableValues: sqlCommandVariableValues, deploymentOptions: deploymentOptions, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode }; return this.runWithErrorHandling(contracts.DeployRequest.type, params); } - public async generateDeployScript(packageFilePath: string, targetDatabaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: mssql.DeploymentOptions): Promise { + public async generateDeployScript(packageFilePath: string, targetDatabaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map, deploymentOptions?: mssql.DeploymentOptions): Promise { const params: contracts.GenerateDeployScriptParams = { packageFilePath: packageFilePath, databaseName: targetDatabaseName, sqlCommandVariableValues: sqlCommandVariableValues, deploymentOptions: deploymentOptions, ownerUri: ownerUri, taskExecutionMode: taskExecutionMode }; return this.runWithErrorHandling(contracts.GenerateDeployScriptRequest.type, params); } @@ -84,7 +84,7 @@ export class DacFxService extends BaseService implements mssql.IDacFxService { return this.runWithErrorHandling(contracts.ParseTSqlScriptRequest.type, params); } - public async savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Record, deploymentOptions?: mssql.DeploymentOptions): Promise { + public async savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map, deploymentOptions?: mssql.DeploymentOptions): Promise { const params: contracts.SavePublishProfileParams = { profilePath, databaseName, connectionString, sqlCommandVariableValues, deploymentOptions }; return this.runWithErrorHandling(contracts.SavePublishProfileRequest.type, params); } diff --git a/extensions/mssql/src/mssql.d.ts b/extensions/mssql/src/mssql.d.ts index a333b071cf..ac570d4fb4 100644 --- a/extensions/mssql/src/mssql.d.ts +++ b/extensions/mssql/src/mssql.d.ts @@ -236,13 +236,13 @@ declare module 'mssql' { importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable; extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable; createProjectFromDatabase(databaseName: string, targetFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, extractTarget: ExtractTarget, taskExecutionMode: azdata.TaskExecutionMode, includePermissions?: boolean): Thenable; - deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; - generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; + deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map, deploymentOptions?: DeploymentOptions): Thenable; + generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode, sqlCommandVariableValues?: Map, deploymentOptions?: DeploymentOptions): Thenable; generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable; getOptionsFromProfile(profilePath: string): Thenable; validateStreamingJob(packageFilePath: string, createStreamingJobTsql: string): Thenable; parseTSqlScript(filePath: string, databaseSchemaProvider: string): Thenable; - savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; + savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map, deploymentOptions?: DeploymentOptions): Thenable; } export interface DacFxResult extends azdata.ResultStatus { diff --git a/extensions/sql-database-projects/src/common/utils.ts b/extensions/sql-database-projects/src/common/utils.ts index 267e52a754..9e656fdf30 100644 --- a/extensions/sql-database-projects/src/common/utils.ts +++ b/extensions/sql-database-projects/src/common/utils.ts @@ -170,8 +170,8 @@ export function systemDatabaseToString(systemDb: mssql.SystemDatabase): string { * @param xmlDoc xml doc to read SQLCMD variables from. Format must be the same that sqlproj and publish profiles use * @param publishProfile true if reading from publish profile */ -export function readSqlCmdVariables(xmlDoc: Document, publishProfile: boolean): Record { - let sqlCmdVariables: Record = {}; +export function readSqlCmdVariables(xmlDoc: Document, publishProfile: boolean): Map { + let sqlCmdVariables: Map = new Map(); for (let i = 0; i < xmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable)?.length; i++) { const sqlCmdVar = xmlDoc.documentElement.getElementsByTagName(constants.SqlCmdVariable)[i]; const varName = sqlCmdVar.getAttribute(constants.Include)!; @@ -181,11 +181,11 @@ export function readSqlCmdVariables(xmlDoc: Document, publishProfile: boolean): // are local variable values you can set in VS in the properties. Since we don't support that in ADS, only DefaultValue is supported for sqlproj. if (!publishProfile && sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0] !== undefined) { // project file path - sqlCmdVariables[varName] = sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0].childNodes[0].nodeValue!; + sqlCmdVariables.set(varName, sqlCmdVar.getElementsByTagName(constants.DefaultValue)[0].childNodes[0].nodeValue!); } else { // profile path - sqlCmdVariables[varName] = sqlCmdVar.getElementsByTagName(constants.Value)[0].childNodes[0].nodeValue!; + sqlCmdVariables.set(varName, sqlCmdVar.getElementsByTagName(constants.Value)[0].childNodes[0].nodeValue!); } } diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index 0b7f9598ce..0ee1db1bd1 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -225,17 +225,17 @@ export class ProjectsController { if (projectTypeId === constants.edgeSqlDatabaseProjectTypeId) { const project = await Project.openProject(newProjFilePath); - await this.addFileToProjectFromTemplate(project, templates.get(ItemType.table), 'DataTable.sql', { 'OBJECT_NAME': 'DataTable' }); - await this.addFileToProjectFromTemplate(project, templates.get(ItemType.dataSource), 'EdgeHubInputDataSource.sql', { 'OBJECT_NAME': 'EdgeHubInputDataSource', 'LOCATION': 'edgehub://' }); - await this.addFileToProjectFromTemplate(project, templates.get(ItemType.dataSource), 'SqlOutputDataSource.sql', { 'OBJECT_NAME': 'SqlOutputDataSource', 'LOCATION': 'sqlserver://tcp:.,1433' }); - await this.addFileToProjectFromTemplate(project, templates.get(ItemType.fileFormat), 'StreamFileFormat.sql', { 'OBJECT_NAME': 'StreamFileFormat' }); - await this.addFileToProjectFromTemplate(project, templates.get(ItemType.externalStream), 'EdgeHubInputStream.sql', { 'OBJECT_NAME': 'EdgeHubInputStream', 'DATA_SOURCE_NAME': 'EdgeHubInputDataSource', 'LOCATION': 'input', 'OPTIONS': ',\n\tFILE_FORMAT = StreamFileFormat' }); - await this.addFileToProjectFromTemplate(project, templates.get(ItemType.externalStream), 'SqlOutputStream.sql', { 'OBJECT_NAME': 'SqlOutputStream', 'DATA_SOURCE_NAME': 'SqlOutputDataSource', 'LOCATION': 'TSQLStreaming.dbo.DataTable', 'OPTIONS': '' }); - await this.addFileToProjectFromTemplate(project, templates.get(ItemType.externalStreamingJob), 'EdgeStreamingJob.sql', { 'OBJECT_NAME': 'EdgeStreamingJob' }); + await this.addFileToProjectFromTemplate(project, templates.get(ItemType.table), 'DataTable.sql', new Map([['OBJECT_NAME', 'DataTable']])); + await this.addFileToProjectFromTemplate(project, templates.get(ItemType.dataSource), 'EdgeHubInputDataSource.sql', new Map([['OBJECT_NAME', 'EdgeHubInputDataSource'], ['LOCATION', 'edgehub://']])); + await this.addFileToProjectFromTemplate(project, templates.get(ItemType.dataSource), 'SqlOutputDataSource.sql', new Map([['OBJECT_NAME', 'SqlOutputDataSource'], ['LOCATION', 'sqlserver://tcp:.,1433']])); + await this.addFileToProjectFromTemplate(project, templates.get(ItemType.fileFormat), 'StreamFileFormat.sql', new Map([['OBJECT_NAME', 'StreamFileFormat']])); + await this.addFileToProjectFromTemplate(project, templates.get(ItemType.externalStream), 'EdgeHubInputStream.sql', new Map([['OBJECT_NAME', 'EdgeHubInputStream'], ['DATA_SOURCE_NAME', 'EdgeHubInputDataSource'], ['LOCATION', 'input'], ['OPTIONS', ',\n\tFILE_FORMAT = StreamFileFormat']])); + await this.addFileToProjectFromTemplate(project, templates.get(ItemType.externalStream), 'SqlOutputStream.sql', new Map([['OBJECT_NAME', 'SqlOutputStream'], ['DATA_SOURCE_NAME', 'SqlOutputDataSource'], ['LOCATION', 'TSQLStreaming.dbo.DataTable'], ['OPTIONS', '']])); + await this.addFileToProjectFromTemplate(project, templates.get(ItemType.externalStreamingJob), 'EdgeStreamingJob.sql', new Map([['OBJECT_NAME', 'EdgeStreamingJob']])); } } - private async addFileToProjectFromTemplate(project: ISqlProject, itemType: templates.ProjectScriptType, relativePath: string, expansionMacros: Record): Promise { + private async addFileToProjectFromTemplate(project: ISqlProject, itemType: templates.ProjectScriptType, relativePath: string, expansionMacros: Map): Promise { const newFileText = templates.macroExpansion(itemType.templateScript, expansionMacros); const absolutePath = path.join(project.projectFolderPath, relativePath) await utils.ensureFileExists(absolutePath, newFileText); @@ -587,7 +587,7 @@ export class ProjectsController { const timeToPublish = actionEndTime - actionStartTime; telemetryProps.actionDuration = timeToPublish.toString(); telemetryProps.totalDuration = (actionEndTime - buildStartTime).toString(); - telemetryProps.sqlcmdVariablesCount = Object.keys(project.sqlCmdVariables).length.toString(); + telemetryProps.sqlcmdVariablesCount = project.sqlCmdVariables.size.toString(); telemetryProps.projectTargetPLatform = project.getProjectTargetVersion(); const currentPublishIndex = this.publishInfo.findIndex(d => d.startDate === currentPublishTimeInfo); @@ -750,7 +750,7 @@ export class ProjectsController { } try { - const absolutePath = await this.addFileToProjectFromTemplate(project, itemType, relativeFilePath, { 'OBJECT_NAME': itemObjectName }); + const absolutePath = await this.addFileToProjectFromTemplate(project, itemType, relativeFilePath, new Map([['OBJECT_NAME', itemObjectName]])); TelemetryReporter.createActionEvent(TelemetryViews.ProjectTree, TelemetryActions.addItemFromTree) .withAdditionalProperties(telemetryProps) @@ -957,7 +957,7 @@ export class ProjectsController { const node = context.element as SqlCmdVariableTreeItem; const project = await this.getProjectFromContext(node); const variableName = node.friendlyName; - const originalValue = project.sqlCmdVariables[variableName]; + const originalValue = project.sqlCmdVariables.get(variableName); const newValue = await vscode.window.showInputBox( { @@ -986,7 +986,7 @@ export class ProjectsController { title: constants.enterNewSqlCmdVariableName, ignoreFocusOut: true, validateInput: (value) => { - return this.sqlCmdVariableNameAlreadyExists(value, project) ? constants.sqlcmdVariableAlreadyExists : undefined; + return project.sqlCmdVariables.has(value) ? constants.sqlcmdVariableAlreadyExists : undefined; } }); @@ -1008,10 +1008,6 @@ export class ProjectsController { this.refreshProjectsTree(context); } - private sqlCmdVariableNameAlreadyExists(newVariableName: string, project: Project): boolean { - return Object.keys(project.sqlCmdVariables).findIndex(v => v === newVariableName) !== -1; - } - private getDatabaseReference(project: Project, context: BaseProjectTreeItem): IDatabaseReferenceProjectEntry | undefined { const databaseReference = context as DatabaseReferenceTreeItem; diff --git a/extensions/sql-database-projects/src/dialogs/publishDatabaseDialog.ts b/extensions/sql-database-projects/src/dialogs/publishDatabaseDialog.ts index 661bb8fc28..8d991c4bee 100644 --- a/extensions/sql-database-projects/src/dialogs/publishDatabaseDialog.ts +++ b/extensions/sql-database-projects/src/dialogs/publishDatabaseDialog.ts @@ -53,7 +53,7 @@ export class PublishDatabaseDialog { private existingServerSelected: boolean = true; private connectionId: string | undefined; private connectionIsDataSource: boolean | undefined; - private sqlCmdVars: Record | undefined; + private sqlCmdVars: Map | undefined; private deploymentOptions: DeploymentOptions | undefined; private profileUsed: boolean = false; private serverName: string | undefined; @@ -70,7 +70,7 @@ export class PublishDatabaseDialog { public publishToContainer: ((proj: Project, profile: IPublishToDockerSettings) => any) | undefined; public generateScript: ((proj: Project, profile: ISqlProjectPublishSettings) => any) | undefined; public readPublishProfile: ((profileUri: vscode.Uri) => any) | undefined; - public savePublishProfile: ((profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions) => any) | undefined; + public savePublishProfile: ((profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map, deploymentOptions?: DeploymentOptions) => any) | undefined; constructor(private project: Project) { this.dialog = utils.getAzdataApi()!.window.createModelViewDialog(constants.publishDialogName, 'sqlProjectPublishDialog'); @@ -190,7 +190,7 @@ export class PublishDatabaseDialog { }); // add SQLCMD variables table if the project has any - if (Object.keys(this.project.sqlCmdVariables).length > 0) { + if (this.project.sqlCmdVariables.size > 0) { this.formBuilder.addFormItem(this.sqlCmdVariablesFormComponentGroup); } @@ -311,9 +311,9 @@ export class PublishDatabaseDialog { return this.deploymentOptions; } - public getSqlCmdVariablesForPublish(): Record { + public getSqlCmdVariablesForPublish(): Map { // get SQLCMD variables from table - let sqlCmdVariables = { ...this.sqlCmdVars }; + let sqlCmdVariables = this.sqlCmdVars ?? new Map(); return sqlCmdVariables; } @@ -681,9 +681,9 @@ export class PublishDatabaseDialog { }).component(); table.onDataChanged(() => { - this.sqlCmdVars = {}; + this.sqlCmdVars = new Map(); table.dataValues?.forEach((row) => { - (>this.sqlCmdVars)[row[0].value] = row[1].value; + this.sqlCmdVars?.set(row[0].value, row[1].value); }); this.updateRevertSqlCmdVarsButtonState(); @@ -708,7 +708,7 @@ export class PublishDatabaseDialog { loadSqlCmdVarsButton.onDidClick(async () => { for (const varName in this.sqlCmdVars) { - this.sqlCmdVars[varName] = this.getDefaultSqlCmdValue(varName); + this.sqlCmdVars.set(varName, this.getDefaultSqlCmdValue(varName)); } const data = this.convertSqlCmdVarsToTableFormat(this.sqlCmdVars!); @@ -729,7 +729,7 @@ export class PublishDatabaseDialog { * @returns value defined in the sqlproj file, or blank string if not defined */ private getDefaultSqlCmdValue(varName: string): string { - return Object.keys(this.project.sqlCmdVariables).includes(varName) ? this.project.sqlCmdVariables[varName] : ''; + return this.project.sqlCmdVariables.get(varName) ?? ''; } private createSelectConnectionButton(view: azdataType.ModelView): azdataType.Component { @@ -804,18 +804,18 @@ export class PublishDatabaseDialog { // set options coming from the publish profiles to deployment options this.setDeploymentOptions(result.options); - if (Object.keys(result.sqlCmdVariables).length) { + if ((>result.sqlCmdVariables).size) { // add SQLCMD Variables table if it wasn't there before and the profile had sqlcmd variables - if (Object.keys(this.project.sqlCmdVariables).length === 0 && Object.keys(>this.sqlCmdVars).length === 0) { + if (this.project.sqlCmdVariables.size === 0 && this.sqlCmdVars?.size === 0) { this.formBuilder?.addFormItem(this.sqlCmdVariablesFormComponentGroup); } - } else if (Object.keys(this.project.sqlCmdVariables).length === 0) { + } else if (this.project.sqlCmdVariables.size === 0) { // remove the table if there are no SQLCMD variables in the project and loaded profile this.formBuilder?.removeFormItem(this.sqlCmdVariablesFormComponentGroup); } for (let key in result.sqlCmdVariables) { - (>this.sqlCmdVars)[key] = result.sqlCmdVariables[key]; + this.sqlCmdVars?.set(key, result.sqlCmdVariableColumn.get(key)); } this.updateRevertSqlCmdVarsButtonState(); @@ -880,10 +880,10 @@ export class PublishDatabaseDialog { return saveProfileAsButton; } - private convertSqlCmdVarsToTableFormat(sqlCmdVars: Record): azdataType.DeclarativeTableCellValue[][] { + private convertSqlCmdVarsToTableFormat(sqlCmdVars: Map): azdataType.DeclarativeTableCellValue[][] { let data = []; for (let key in sqlCmdVars) { - data.push([{ value: key }, { value: sqlCmdVars[key] }]); + data.push([{ value: key }, { value: sqlCmdVars.get(key)! }]); } return data; @@ -901,7 +901,7 @@ export class PublishDatabaseDialog { let revertButtonEnabled = false; for (const varName in this.sqlCmdVars) { - if (this.sqlCmdVars![varName] !== this.getDefaultSqlCmdValue(varName)) { + if (this.sqlCmdVars!.get(varName) !== this.getDefaultSqlCmdValue(varName)) { revertButtonEnabled = true; break; } @@ -936,7 +936,7 @@ export class PublishDatabaseDialog { private allSqlCmdVariablesFilled(): boolean { for (let key in this.sqlCmdVars) { - if (this.sqlCmdVars[key] === '' || this.sqlCmdVars[key] === undefined) { + if (this.sqlCmdVars.get(key) === '' || this.sqlCmdVars.get(key) === undefined) { return false; } } diff --git a/extensions/sql-database-projects/src/dialogs/publishDatabaseQuickpick.ts b/extensions/sql-database-projects/src/dialogs/publishDatabaseQuickpick.ts index 875c20f8e0..5c683c3816 100644 --- a/extensions/sql-database-projects/src/dialogs/publishDatabaseQuickpick.ts +++ b/extensions/sql-database-projects/src/dialogs/publishDatabaseQuickpick.ts @@ -163,7 +163,7 @@ export async function getPublishDatabaseSettings(project: ISqlProject, promptFor // project file (if they exist) let sqlCmdVariables = Object.assign({}, project.sqlCmdVariables, publishProfile?.sqlCmdVariables); - if (Object.keys(sqlCmdVariables).length > 0) { + if (sqlCmdVariables.size > 0) { // Continually loop here, allowing the user to modify SQLCMD variables one // at a time until they're done (either by selecting the "Done" option or // escaping out of the quick pick dialog). Users can modify each variable @@ -173,7 +173,7 @@ export async function getPublishDatabaseSettings(project: ISqlProject, promptFor const quickPickItems = Object.keys(sqlCmdVariables).map(key => { return { label: key, - description: sqlCmdVariables[key], + description: sqlCmdVariables.get(key), key: key } as vscode.QuickPickItem & { key?: string, isResetAllVars?: boolean, isDone?: boolean }; }); @@ -192,12 +192,12 @@ export async function getPublishDatabaseSettings(project: ISqlProject, promptFor const newValue = await vscode.window.showInputBox( { title: constants.enterNewValueForVar(sqlCmd.key), - value: sqlCmdVariables[sqlCmd.key], + value: sqlCmdVariables.get(sqlCmd.key), ignoreFocusOut: true } ); if (newValue) { - sqlCmdVariables[sqlCmd.key] = newValue; + sqlCmdVariables.set(sqlCmd.key, newValue); } } else if (sqlCmd.isResetAllVars) { sqlCmdVariables = Object.assign({}, project.sqlCmdVariables, publishProfile?.sqlCmdVariables); diff --git a/extensions/sql-database-projects/src/models/deploy/publishSettings.ts b/extensions/sql-database-projects/src/models/deploy/publishSettings.ts index b480e6e672..181f4b4652 100644 --- a/extensions/sql-database-projects/src/models/deploy/publishSettings.ts +++ b/extensions/sql-database-projects/src/models/deploy/publishSettings.ts @@ -16,7 +16,7 @@ export interface ISqlProjectPublishSettings { databaseName: string; serverName: string; connectionUri: string; - sqlCmdVariables?: Record; + sqlCmdVariables?: Map; deploymentOptions?: DeploymentOptions; profileUsed?: boolean; } diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index c1970aad6b..e0950cc9a2 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -44,7 +44,7 @@ export class Project implements ISqlProject { private _folders: FileProjectEntry[] = []; private _dataSources: DataSource[] = []; private _databaseReferences: IDatabaseReferenceProjectEntry[] = []; - private _sqlCmdVariables: Record = {}; + private _sqlCmdVariables: Map = new Map(); private _preDeployScripts: FileProjectEntry[] = []; private _postDeployScripts: FileProjectEntry[] = []; private _noneDeployScripts: FileProjectEntry[] = []; @@ -97,7 +97,7 @@ export class Project implements ISqlProject { return this._databaseReferences; } - public get sqlCmdVariables(): Record { + public get sqlCmdVariables(): Map { return this._sqlCmdVariables; } @@ -267,10 +267,10 @@ export class Project implements ISqlProject { throw new Error(constants.errorReadingProject(constants.sqlCmdVariables, this.projectFilePath, sqlcmdVariablesResult.errorMessage)); } - this._sqlCmdVariables = {}; + this._sqlCmdVariables = new Map(); for (const variable of sqlcmdVariablesResult.sqlCmdVariables) { - this._sqlCmdVariables[variable.varName] = variable.defaultValue; // store the default value that's specified in the .sqlproj + this._sqlCmdVariables.set(variable.varName, variable.defaultValue); // store the default value that's specified in the .sqlproj } } @@ -435,7 +435,7 @@ export class Project implements ISqlProject { private resetProject(): void { this._files = []; this._databaseReferences = []; - this._sqlCmdVariables = {}; + this._sqlCmdVariables = new Map(); this._preDeployScripts = []; this._postDeployScripts = []; this._noneDeployScripts = []; @@ -843,7 +843,8 @@ export class Project implements ISqlProject { * @param defaultValue */ public async addSqlCmdVariable(name: string, defaultValue: string): Promise { - await this.sqlProjService.addSqlCmdVariable(this.projectFilePath, name, defaultValue); + const result = await this.sqlProjService.addSqlCmdVariable(this.projectFilePath, name, defaultValue); + this.throwIfFailed(result); await this.readSqlCmdVariables(); } @@ -853,7 +854,8 @@ export class Project implements ISqlProject { * @param defaultValue */ public async updateSqlCmdVariable(name: string, defaultValue: string): Promise { - await this.sqlProjService.updateSqlCmdVariable(this.projectFilePath, name, defaultValue); + const result = await this.sqlProjService.updateSqlCmdVariable(this.projectFilePath, name, defaultValue); + this.throwIfFailed(result); await this.readSqlCmdVariables(); } diff --git a/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts b/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts index 29d53100a8..9e5f8e17ff 100644 --- a/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts +++ b/extensions/sql-database-projects/src/models/publishProfile/publishProfile.ts @@ -20,7 +20,7 @@ export interface PublishProfile { serverName: string; connectionId: string; connection: string; - sqlCmdVariables: Record; + sqlCmdVariables: Map; options?: mssql.DeploymentOptions | vscodeMssql.DeploymentOptions; } @@ -60,7 +60,7 @@ export async function load(profileUri: vscode.Uri, dacfxService: utils.IDacFxSer .withAdditionalProperties({ hasTargetDbName: (!!targetDbName).toString(), hasConnectionString: (!!connectionInfo?.connectionId).toString(), - hasSqlCmdVariables: (Object.keys(sqlCmdVariables).length > 0).toString() + hasSqlCmdVariables: (sqlCmdVariables.size > 0).toString() }).send(); return { @@ -129,7 +129,7 @@ async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string /** * saves publish settings to the specified profile file */ -export async function savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Record, deploymentOptions?: mssql.DeploymentOptions): Promise { +export async function savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map, deploymentOptions?: mssql.DeploymentOptions): Promise { const dacFxService = await utils.getDacFxService(); await dacFxService.savePublishProfile(profilePath, databaseName, connectionString, sqlCommandVariableValues, deploymentOptions); } diff --git a/extensions/sql-database-projects/src/models/tree/sqlcmdVariableTreeItem.ts b/extensions/sql-database-projects/src/models/tree/sqlcmdVariableTreeItem.ts index cf06d9810d..d0151ac704 100644 --- a/extensions/sql-database-projects/src/models/tree/sqlcmdVariableTreeItem.ts +++ b/extensions/sql-database-projects/src/models/tree/sqlcmdVariableTreeItem.ts @@ -23,21 +23,19 @@ export class SqlCmdVariablesTreeItem extends BaseProjectTreeItem { * @param sqlCmdVariables Collection of SQLCMD variables in the project * @param project */ - constructor(projectNodeName: string, sqlprojUri: vscode.Uri, sqlCmdVariables: Record) { + constructor(projectNodeName: string, sqlprojUri: vscode.Uri, sqlCmdVariables: Map) { super(vscode.Uri.file(path.join(projectNodeName, constants.sqlcmdVariablesNodeName)), sqlprojUri); this.construct(sqlCmdVariables); } - private construct(sqlCmdVariables: Record) { + private construct(sqlCmdVariables: Map) { if (!sqlCmdVariables) { return; } - for (const sqlCmdVariable of Object.keys(sqlCmdVariables)) { - if (sqlCmdVariable) { - this.sqlcmdVariableTreeItems.push(new SqlCmdVariableTreeItem(sqlCmdVariable, this.relativeProjectUri, this.projectFileUri)); - } + for (const sqlCmdVariable of sqlCmdVariables.keys()) { + this.sqlcmdVariableTreeItems.push(new SqlCmdVariableTreeItem(sqlCmdVariable, this.relativeProjectUri, this.projectFileUri)); } } diff --git a/extensions/sql-database-projects/src/sqldbproj.d.ts b/extensions/sql-database-projects/src/sqldbproj.d.ts index e153451970..77a0e1a8ab 100644 --- a/extensions/sql-database-projects/src/sqldbproj.d.ts +++ b/extensions/sql-database-projects/src/sqldbproj.d.ts @@ -248,7 +248,7 @@ declare module 'sqldbproj' { /** * SqlCmd variables and their values */ - readonly sqlCmdVariables: Record; + readonly sqlCmdVariables: Map; /** * Pre-deployment scripts in this project diff --git a/extensions/sql-database-projects/src/templates/templates.ts b/extensions/sql-database-projects/src/templates/templates.ts index 6903e1450b..e3969f28d0 100644 --- a/extensions/sql-database-projects/src/templates/templates.ts +++ b/extensions/sql-database-projects/src/templates/templates.ts @@ -13,14 +13,14 @@ export let newSdkSqlProjectTemplate: string; // Object maps -let scriptTypeMap: Record = {}; +let scriptTypeMap: Map = new Map(); export function get(key: string): ProjectScriptType { - if (Object.keys(scriptTypeMap).length === 0) { + if (scriptTypeMap.size === 0) { throw new Error('Templates must be loaded from file before attempting to use.'); } - return scriptTypeMap[key.toLocaleLowerCase()]; + return scriptTypeMap.get(key.toLocaleLowerCase())!; } let scriptTypes: ProjectScriptType[] = []; @@ -52,26 +52,26 @@ export async function loadTemplates(templateFolderPath: string) { ]); for (const scriptType of scriptTypes) { - if (Object.keys(scriptTypeMap).find(s => s === scriptType.type.toLocaleLowerCase() || s === scriptType.friendlyName.toLocaleLowerCase())) { + if (scriptTypeMap.has(scriptType.type.toLocaleLowerCase()) || scriptTypeMap.has(scriptType.friendlyName.toLocaleLowerCase())) { throw new Error(`Script type map already contains ${scriptType.type} or its friendlyName.`); } - scriptTypeMap[scriptType.type.toLocaleLowerCase()] = scriptType; - scriptTypeMap[scriptType.friendlyName.toLocaleLowerCase()] = scriptType; + scriptTypeMap.set(scriptType.type.toLocaleLowerCase(), scriptType); + scriptTypeMap.set(scriptType.friendlyName.toLocaleLowerCase(), scriptType); } } -export function macroExpansion(template: string, macroDict: Record): string { +export function macroExpansion(template: string, macroDict: Map): string { const macroIndicator = '@@'; let output = template; for (const macro in macroDict) { // check if value contains the macroIndicator, which could break expansion for successive macros - if (macroDict[macro].includes(macroIndicator)) { - throw new Error(`Macro value ${macroDict[macro]} is invalid because it contains ${macroIndicator}`); + if (macroDict.get(macro)!.includes(macroIndicator)) { + throw new Error(`Macro value ${macroDict.get(macro)} is invalid because it contains ${macroIndicator}`); } - output = output.replace(new RegExp(macroIndicator + macro + macroIndicator, 'g'), macroDict[macro]); + output = output.replace(new RegExp(macroIndicator + macro + macroIndicator, 'g'), macroDict.get(macro)!); } return output; @@ -104,6 +104,6 @@ export class ProjectScriptType { * For testing purposes only */ export function reset() { - scriptTypeMap = {}; + scriptTypeMap = new Map(); scriptTypes = []; } diff --git a/extensions/sql-database-projects/src/test/dialogs/publishDatabaseDialog.test.ts b/extensions/sql-database-projects/src/test/dialogs/publishDatabaseDialog.test.ts index 23181702f3..127ab9ed36 100644 --- a/extensions/sql-database-projects/src/test/dialogs/publishDatabaseDialog.test.ts +++ b/extensions/sql-database-projects/src/test/dialogs/publishDatabaseDialog.test.ts @@ -83,10 +83,10 @@ describe('Publish Database Dialog', () => { databaseName: 'MockDatabaseName', serverName: 'MockServer', connectionUri: 'Mock|Connection|Uri', - sqlCmdVariables: { - 'ProdDatabaseName': 'MyProdDatabase', - 'BackupDatabaseName': 'MyBackupDatabase' - }, + sqlCmdVariables: new Map([ + ['ProdDatabaseName', 'MyProdDatabase'], + ['BackupDatabaseName', 'MyBackupDatabase'] + ]), deploymentOptions: mockDacFxOptionsResult.deploymentOptions, profileUsed: false }; @@ -100,10 +100,10 @@ describe('Publish Database Dialog', () => { databaseName: 'MockDatabaseName', serverName: 'MockServer', connectionUri: 'Mock|Connection|Uri', - sqlCmdVariables: { - 'ProdDatabaseName': 'MyProdDatabase', - 'BackupDatabaseName': 'MyBackupDatabase' - }, + sqlCmdVariables: new Map([ + ['ProdDatabaseName', 'MyProdDatabase'], + ['BackupDatabaseName', 'MyBackupDatabase'] + ]), deploymentOptions: mockDacFxOptionsResult.deploymentOptions, profileUsed: false }; @@ -128,10 +128,10 @@ describe('Publish Database Dialog', () => { databaseName: 'MockDatabaseName', serverName: 'localhost', connectionUri: '', - sqlCmdVariables: { - 'ProdDatabaseName': 'MyProdDatabase', - 'BackupDatabaseName': 'MyBackupDatabase' - }, + sqlCmdVariables: new Map([ + ['ProdDatabaseName', 'MyProdDatabase'], + ['BackupDatabaseName', 'MyBackupDatabase'] + ]), deploymentOptions: mockDacFxOptionsResult.deploymentOptions, profileUsed: false } diff --git a/extensions/sql-database-projects/src/test/project.test.ts b/extensions/sql-database-projects/src/test/project.test.ts index a7882eebfb..05e54a8e0c 100644 --- a/extensions/sql-database-projects/src/test/project.test.ts +++ b/extensions/sql-database-projects/src/test/project.test.ts @@ -48,9 +48,9 @@ describe('Project: sqlproj content operations', function (): void { 'Views\\User']); // SqlCmdVariables - should(Object.keys(project.sqlCmdVariables).length).equal(2); - should(project.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); - should(project.sqlCmdVariables['BackupDatabaseName']).equal('MyBackupDatabase'); + should(project.sqlCmdVariables.size).equal(2); + should(project.sqlCmdVariables.get('ProdDatabaseName')).equal('MyProdDatabase'); + should(project.sqlCmdVariables.get('BackupDatabaseName')).equal('MyBackupDatabase'); // Database references // should only have one database reference even though there are two master.dacpac references (1 for ADS and 1 for SSDT) @@ -647,7 +647,7 @@ describe('Project: database references', function (): void { // add database reference to a different database on a different server should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); - should(Object.keys(project.sqlCmdVariables).length).equal(0, `There should be no sqlcmd variables to start with. Actual: ${Object.keys(project.sqlCmdVariables).length}`); + should(project.sqlCmdVariables.size).equal(0, `There should be no sqlcmd variables to start with. Actual: ${project.sqlCmdVariables.size}`); await project.addProjectReference({ projectName: 'project1', projectGuid: '', @@ -658,7 +658,7 @@ describe('Project: database references', function (): void { should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to project1'); should(project.databaseReferences[0].referenceName).equal('project1', 'The database reference should be project1'); should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false'); - should(Object.keys(project.sqlCmdVariables).length).equal(0, `There should be no sqlcmd variables added. Actual: ${Object.keys(project.sqlCmdVariables).length}`); + should(project.sqlCmdVariables.size).equal(0, `There should be no sqlcmd variables added. Actual: ${project.sqlCmdVariables.size}`); }); it('Should add a project reference to a different database in the same server correctly', async function (): Promise { @@ -667,7 +667,7 @@ describe('Project: database references', function (): void { // add database reference to a different database on a different server should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); - should(Object.keys(project.sqlCmdVariables).length).equal(0, 'There should be no sqlcmd variables to start with'); + should(project.sqlCmdVariables.size).equal(0, 'There should be no sqlcmd variables to start with'); await project.addProjectReference({ projectName: 'project1', projectGuid: '', @@ -680,7 +680,7 @@ describe('Project: database references', function (): void { should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to project1'); should(project.databaseReferences[0].referenceName).equal('project1', 'The database reference should be project1'); should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false'); - should(Object.keys(project.sqlCmdVariables).length).equal(1, `There should be one new sqlcmd variable added. Actual: ${Object.keys(project.sqlCmdVariables).length}`); + should(project.sqlCmdVariables.size).equal(1, `There should be one new sqlcmd variable added. Actual: ${project.sqlCmdVariables.size}`); }); it('Should add a project reference to a different database in a different server correctly', async function (): Promise { @@ -689,7 +689,7 @@ describe('Project: database references', function (): void { // add database reference to a different database on a different server should(project.databaseReferences.length).equal(0, 'There should be no database references to start with'); - should(Object.keys(project.sqlCmdVariables).length).equal(0, 'There should be no sqlcmd variables to start with'); + should(project.sqlCmdVariables.size).equal(0, 'There should be no sqlcmd variables to start with'); await project.addProjectReference({ projectName: 'project1', projectGuid: '', @@ -704,7 +704,7 @@ describe('Project: database references', function (): void { should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to project1'); should(project.databaseReferences[0].referenceName).equal('project1', 'The database reference should be project1'); should(project.databaseReferences[0].suppressMissingDependenciesErrors).equal(false, 'project.databaseReferences[0].suppressMissingDependenciesErrors should be false'); - should(Object.keys(project.sqlCmdVariables).length).equal(2, `There should be two new sqlcmd variables added. Actual: ${Object.keys(project.sqlCmdVariables).length}`); + should(project.sqlCmdVariables.size).equal(2, `There should be two new sqlcmd variables added. Actual: ${project.sqlCmdVariables.size}`); }); it('Should not allow adding duplicate dacpac references', async function (): Promise { @@ -803,7 +803,7 @@ describe('Project: database references', function (): void { }); should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test3'); should(project.databaseReferences[0].referenceName).equal('test3', 'The database reference should be test3'); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'There should be 2 sqlcmdvars after adding the dacpac reference'); + should(project.sqlCmdVariables.size).equal(2, 'There should be 2 sqlcmdvars after adding the dacpac reference'); // make sure reference to test3.dacpac and SQLCMD variables were added let projFileText = (await fs.readFile(projFilePath)).toString(); @@ -815,7 +815,7 @@ describe('Project: database references', function (): void { // delete reference await project.deleteDatabaseReferenceByEntry(project.databaseReferences[0]); should(project.databaseReferences.length).equal(0, 'There should be no database references after deleting'); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'There should still be 2 sqlcmdvars after deleting the dacpac reference'); + should(project.sqlCmdVariables.size).equal(2, 'There should still be 2 sqlcmdvars after deleting the dacpac reference'); // add reference to the same dacpac again but with different values for the sqlcmd variables await project.addDatabaseReference({ @@ -828,7 +828,7 @@ describe('Project: database references', function (): void { }); should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test3'); should(project.databaseReferences[0].referenceName).equal('test3', 'The database reference should be test3'); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'There should still be 2 sqlcmdvars after adding the dacpac reference again with different sqlcmdvar values'); + should(project.sqlCmdVariables.size).equal(2, 'There should still be 2 sqlcmdvars after adding the dacpac reference again with different sqlcmdvar values'); projFileText = (await fs.readFile(projFilePath)).toString(); should(projFileText).containEql(''); @@ -850,7 +850,7 @@ describe('Project: add SQLCMD Variables', function (): void { it('Should update .sqlproj with new sqlcmd variables', async function (): Promise { const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.openProjectFileBaseline); let project = await Project.openProject(projFilePath); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should have 2 sqlcmd variables when opened'); + should(project.sqlCmdVariables.size).equal(2, 'The project should have 2 sqlcmd variables when opened'); // add a new variable await project.addSqlCmdVariable('TestDatabaseName', 'TestDb'); @@ -858,9 +858,9 @@ describe('Project: add SQLCMD Variables', function (): void { // update value of an existing sqlcmd variable await project.updateSqlCmdVariable('ProdDatabaseName', 'NewProdName'); - should(Object.keys(project.sqlCmdVariables).length).equal(3, 'There should be 3 sqlcmd variables after adding TestDatabaseName'); - should(project.sqlCmdVariables['TestDatabaseName']).equal('TestDb', 'Value of TestDatabaseName should be TestDb'); - should(project.sqlCmdVariables['ProdDatabaseName']).equal('NewProdName', 'ProdDatabaseName value should have been updated to the new value'); + should(project.sqlCmdVariables.size).equal(3, 'There should be 3 sqlcmd variables after adding TestDatabaseName'); + should(project.sqlCmdVariables.get('TestDatabaseName')).equal('TestDb', 'Value of TestDatabaseName should be TestDb'); + should(project.sqlCmdVariables.get('ProdDatabaseName')).equal('NewProdName', 'ProdDatabaseName value should have been updated to the new value'); }); }); diff --git a/extensions/sql-database-projects/src/test/projectController.test.ts b/extensions/sql-database-projects/src/test/projectController.test.ts index bda997d08f..3460828faf 100644 --- a/extensions/sql-database-projects/src/test/projectController.test.ts +++ b/extensions/sql-database-projects/src/test/projectController.test.ts @@ -303,7 +303,7 @@ describe('ProjectsController', function (): void { }); it('Should exclude nested ProjectEntry from node', async function (): Promise { - let proj = await testUtils.createTestProject(this.test, templates.newSqlProjectTemplate); + let proj = await testUtils.createTestSqlProject(this.test); const setupResult = await setupDeleteExcludeTest(proj); const scriptEntry = setupResult[0], projTreeRoot = setupResult[1], preDeployEntry = setupResult[2], postDeployEntry = setupResult[3], noneEntry = setupResult[4]; @@ -997,14 +997,14 @@ describe('ProjectsController', function (): void { const projController = new ProjectsController(testContext.outputChannel); const projRoot = new ProjectRootTreeItem(project); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should start with 2 sqlcmd variables'); + should(project.sqlCmdVariables.size).equal(2, 'The project should start with 2 sqlcmd variables'); sinon.stub(vscode.window, 'showWarningMessage').returns(Promise.resolve('Cancel')); await projController.delete(createWorkspaceTreeItem(projRoot.children.find(x => x.friendlyName === constants.sqlcmdVariablesNodeName)!.children[0])); // reload project project = await Project.openProject(project.projectFilePath); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should still have 2 sqlcmd variables if no was selected'); + should(project.sqlCmdVariables.size).equal(2, 'The project should still have 2 sqlcmd variables if no was selected'); sinon.restore(); sinon.stub(vscode.window, 'showWarningMessage').returns(Promise.resolve('Yes')); @@ -1012,7 +1012,7 @@ describe('ProjectsController', function (): void { // reload project project = await Project.openProject(project.projectFilePath); - should(Object.keys(project.sqlCmdVariables).length).equal(1, 'The project should only have 1 sqlcmd variable after deletion'); + should(project.sqlCmdVariables.size).equal(1, 'The project should only have 1 sqlcmd variable after deletion'); }); it('Should add sqlcmd variable', async function (): Promise { @@ -1023,7 +1023,7 @@ describe('ProjectsController', function (): void { const projController = new ProjectsController(testContext.outputChannel); const projRoot = new ProjectRootTreeItem(project); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should start with 2 sqlcmd variables'); + should(project.sqlCmdVariables.size).equal(2, 'The project should start with 2 sqlcmd variables'); const inputBoxStub = sinon.stub(vscode.window, 'showInputBox'); inputBoxStub.resolves(''); @@ -1031,7 +1031,7 @@ describe('ProjectsController', function (): void { // reload project project = await Project.openProject(project.projectFilePath); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should still have 2 sqlcmd variables if no name was provided'); + should(project.sqlCmdVariables.size).equal(2, 'The project should still have 2 sqlcmd variables if no name was provided'); inputBoxStub.reset(); inputBoxStub.onFirstCall().resolves('newVariable'); @@ -1040,7 +1040,7 @@ describe('ProjectsController', function (): void { // reload project project = await Project.openProject(project.projectFilePath); - should(Object.keys(project.sqlCmdVariables).length).equal(3, 'The project should have 3 sqlcmd variable after adding a new one'); + should(project.sqlCmdVariables.size).equal(3, 'The project should have 3 sqlcmd variable after adding a new one'); }); it('Should update sqlcmd variable', async function (): Promise { @@ -1051,18 +1051,18 @@ describe('ProjectsController', function (): void { const projController = new ProjectsController(testContext.outputChannel); const projRoot = new ProjectRootTreeItem(project); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should start with 2 sqlcmd variables'); + should(project.sqlCmdVariables.size).equal(2, 'The project should start with 2 sqlcmd variables'); const inputBoxStub = sinon.stub(vscode.window, 'showInputBox'); inputBoxStub.resolves(''); const sqlcmdVarToUpdate = projRoot.children.find(x => x.friendlyName === constants.sqlcmdVariablesNodeName)!.children[0]; - const originalValue = project.sqlCmdVariables[sqlcmdVarToUpdate.friendlyName]; + const originalValue = project.sqlCmdVariables.get(sqlcmdVarToUpdate.friendlyName); await projController.editSqlCmdVariable(createWorkspaceTreeItem(sqlcmdVarToUpdate)); // reload project project = await Project.openProject(project.projectFilePath); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should still have 2 sqlcmd variables'); - should(project.sqlCmdVariables[sqlcmdVarToUpdate.friendlyName]).equal(originalValue, 'The value of the sqlcmd variable should not have changed'); + should(project.sqlCmdVariables.size).equal(2, 'The project should still have 2 sqlcmd variables'); + should(project.sqlCmdVariables.get(sqlcmdVarToUpdate.friendlyName)).equal(originalValue, 'The value of the sqlcmd variable should not have changed'); inputBoxStub.reset(); const updatedValue = 'newValue'; @@ -1071,8 +1071,8 @@ describe('ProjectsController', function (): void { // reload project project = await Project.openProject(project.projectFilePath); - should(Object.keys(project.sqlCmdVariables).length).equal(2, 'The project should still have 2 sqlcmd variables'); - should(project.sqlCmdVariables[sqlcmdVarToUpdate.friendlyName]).equal(updatedValue, 'The value of the sqlcmd variable should have been updated'); + should(project.sqlCmdVariables.size).equal(2, 'The project should still have 2 sqlcmd variables'); + should(project.sqlCmdVariables.get(sqlcmdVarToUpdate.friendlyName)).equal(updatedValue, 'The value of the sqlcmd variable should have been updated'); }); }); }); diff --git a/extensions/sql-database-projects/src/test/publishProfile.test.ts b/extensions/sql-database-projects/src/test/publishProfile.test.ts index b9ec29a254..609d95aac8 100644 --- a/extensions/sql-database-projects/src/test/publishProfile.test.ts +++ b/extensions/sql-database-projects/src/test/publishProfile.test.ts @@ -49,8 +49,8 @@ describe('Publish profile tests', function (): void { const result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); should(result.databaseName).equal('targetDb'); - should(Object.keys(result.sqlCmdVariables).length).equal(1); - should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); + should(result.sqlCmdVariables.size).equal(1); + should(result.sqlCmdVariables.get('ProdDatabaseName')).equal('MyProdDatabase'); should(result.connectionId).equal('connId'); should(result.connection).equal('testserver (default)'); should(result.options).equal(mockDacFxOptionsResult.deploymentOptions); @@ -74,8 +74,8 @@ describe('Publish profile tests', function (): void { const result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); should(result.databaseName).equal('targetDb'); - should(Object.keys(result.sqlCmdVariables).length).equal(1); - should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); + should(result.sqlCmdVariables.size).equal(1); + should(result.sqlCmdVariables.get('ProdDatabaseName')).equal('MyProdDatabase'); should(result.connectionId).equal('connId'); should(result.connection).equal('testserver (testUser)'); should(result.options).equal(mockDacFxOptionsResult.deploymentOptions); @@ -89,10 +89,10 @@ describe('Publish profile tests', function (): void { }); const result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object); - should(Object.keys(result.sqlCmdVariables).length).equal(1); + should(result.sqlCmdVariables.size).equal(1); // the profile has both Value and DefaultValue, but Value should be the one used - should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase'); + should(result.sqlCmdVariables.get('ProdDatabaseName')).equal('MyProdDatabase'); }); it('Should throw error when connecting does not work', async function (): Promise { diff --git a/extensions/sql-database-projects/src/test/testContext.ts b/extensions/sql-database-projects/src/test/testContext.ts index 5179470c64..4777a282aa 100644 --- a/extensions/sql-database-projects/src/test/testContext.ts +++ b/extensions/sql-database-projects/src/test/testContext.ts @@ -56,13 +56,13 @@ export class MockDacFxService implements mssql.IDacFxService { public importBacpac(_packageFilePath: string, _databaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } public extractDacpac(_databaseName: string, _packageFilePath: string, _applicationName: string, _applicationVersion: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } public createProjectFromDatabase(_databaseName: string, _targetFilePath: string, _applicationName: string, _applicationVersion: string, _ownerUri: string, _extractTarget: mssql.ExtractTarget, _taskExecutionMode: azdata.TaskExecutionMode, _includePermissions?: boolean): Thenable { return Promise.resolve(mockDacFxResult); } - public deployDacpac(_packageFilePath: string, _targetDatabaseName: string, _upgradeExisting: boolean, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Record, _deploymentOptions?: mssql.DeploymentOptions): Thenable { return Promise.resolve(mockDacFxResult); } - public generateDeployScript(_packageFilePath: string, _targetDatabaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Record, _deploymentOptions?: mssql.DeploymentOptions): Thenable { return Promise.resolve(mockDacFxResult); } + public deployDacpac(_packageFilePath: string, _targetDatabaseName: string, _upgradeExisting: boolean, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Map, _deploymentOptions?: mssql.DeploymentOptions): Thenable { return Promise.resolve(mockDacFxResult); } + public generateDeployScript(_packageFilePath: string, _targetDatabaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Map, _deploymentOptions?: mssql.DeploymentOptions): Thenable { return Promise.resolve(mockDacFxResult); } public generateDeployPlan(_packageFilePath: string, _targetDatabaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } public getOptionsFromProfile(_profilePath: string): Thenable { return Promise.resolve(mockDacFxOptionsResult); } public validateStreamingJob(_packageFilePath: string, _createStreamingJobTsql: string): Thenable { return Promise.resolve(mockDacFxResult); } public parseTSqlScript(_filePath: string, _databaseSchemaProvider: string): Thenable { return Promise.resolve({ containsCreateTableStatement: true }); } - public savePublishProfile(_profilePath: string, _databaseName: string, _connectionString: string, _sqlCommandVariableValues?: Record, _deploymentOptions?: mssql.DeploymentOptions): Thenable { return Promise.resolve(mockSavePublishResult); } + public savePublishProfile(_profilePath: string, _databaseName: string, _connectionString: string, _sqlCommandVariableValues?: Map, _deploymentOptions?: mssql.DeploymentOptions): Thenable { return Promise.resolve(mockSavePublishResult); } } export function createContext(): TestContext { diff --git a/extensions/sql-database-projects/src/test/testUtils.ts b/extensions/sql-database-projects/src/test/testUtils.ts index b56c3b17d2..d909b3d827 100644 --- a/extensions/sql-database-projects/src/test/testUtils.ts +++ b/extensions/sql-database-projects/src/test/testUtils.ts @@ -43,9 +43,7 @@ export async function getTestProjectPath(test: Mocha.Runnable | undefined): Prom export async function createTestSqlProjFile(test: Mocha.Runnable | undefined, contents: string, folderPath?: string): Promise { folderPath = folderPath ?? path.join(await generateTestFolderPath(test), 'TestProject'); - const macroDict: Record = { - 'PROJECT_DSP': constants.defaultDSP - }; + const macroDict: Map = new Map([['PROJECT_DSP', constants.defaultDSP]]); contents = templates.macroExpansion(contents, macroDict); return await createTestFile(test, contents, 'TestProject.sqlproj', folderPath); } diff --git a/extensions/types/vscode-mssql.d.ts b/extensions/types/vscode-mssql.d.ts index aa63ffe4c5..fe5816d81c 100644 --- a/extensions/types/vscode-mssql.d.ts +++ b/extensions/types/vscode-mssql.d.ts @@ -426,12 +426,12 @@ declare module 'vscode-mssql' { importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; createProjectFromDatabase(databaseName: string, targetFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, extractTarget: ExtractTarget, taskExecutionMode: TaskExecutionMode, includePermissions?: boolean): Thenable; - deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; - generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; + deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: TaskExecutionMode, sqlCommandVariableValues?: Map, deploymentOptions?: DeploymentOptions): Thenable; + generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode, sqlCommandVariableValues?: Map, deploymentOptions?: DeploymentOptions): Thenable; generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; getOptionsFromProfile(profilePath: string): Thenable; validateStreamingJob(packageFilePath: string, createStreamingJobTsql: string): Thenable; - savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; + savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map, deploymentOptions?: DeploymentOptions): Thenable; } /**