Swapping Record usage to Map in SQL Projects (#22758)

* Changing SqlCmdVars from Record to Map

* Converting the rest

* Updating tests

* more cleanup

* Updating test to use new test creation API
This commit is contained in:
Benjin Dubishar
2023-04-17 12:56:39 -07:00
committed by GitHub
parent 4a4580e9ef
commit 02e61d1598
21 changed files with 132 additions and 138 deletions

View File

@@ -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<string, string> {
let sqlCmdVariables: Record<string, string> = {};
export function readSqlCmdVariables(xmlDoc: Document, publishProfile: boolean): Map<string, string> {
let sqlCmdVariables: Map<string, string> = 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!);
}
}

View File

@@ -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<string, string>): Promise<string> {
private async addFileToProjectFromTemplate(project: ISqlProject, itemType: templates.ProjectScriptType, relativePath: string, expansionMacros: Map<string, string>): Promise<string> {
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;

View File

@@ -53,7 +53,7 @@ export class PublishDatabaseDialog {
private existingServerSelected: boolean = true;
private connectionId: string | undefined;
private connectionIsDataSource: boolean | undefined;
private sqlCmdVars: Record<string, string> | undefined;
private sqlCmdVars: Map<string, string> | 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<string, string>, deploymentOptions?: DeploymentOptions) => any) | undefined;
public savePublishProfile: ((profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map<string, string>, 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<string, string> {
public getSqlCmdVariablesForPublish(): Map<string, string> {
// 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) => {
(<Record<string, string>>this.sqlCmdVars)[<string>row[0].value] = <string>row[1].value;
this.sqlCmdVars?.set(<string>row[0].value, <string>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 ((<Map<string, string>>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(<Record<string, string>>this.sqlCmdVars).length === 0) {
if (this.project.sqlCmdVariables.size === 0 && this.sqlCmdVars?.size === 0) {
this.formBuilder?.addFormItem(<azdataType.FormComponentGroup>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(<azdataType.FormComponentGroup>this.sqlCmdVariablesFormComponentGroup);
}
for (let key in result.sqlCmdVariables) {
(<Record<string, string>>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<string, string>): azdataType.DeclarativeTableCellValue[][] {
private convertSqlCmdVarsToTableFormat(sqlCmdVars: Map<string, string>): 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;
}
}

View File

@@ -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);

View File

@@ -16,7 +16,7 @@ export interface ISqlProjectPublishSettings {
databaseName: string;
serverName: string;
connectionUri: string;
sqlCmdVariables?: Record<string, string>;
sqlCmdVariables?: Map<string, string>;
deploymentOptions?: DeploymentOptions;
profileUsed?: boolean;
}

View File

@@ -44,7 +44,7 @@ export class Project implements ISqlProject {
private _folders: FileProjectEntry[] = [];
private _dataSources: DataSource[] = [];
private _databaseReferences: IDatabaseReferenceProjectEntry[] = [];
private _sqlCmdVariables: Record<string, string> = {};
private _sqlCmdVariables: Map<string, string> = 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<string, string> {
public get sqlCmdVariables(): Map<string, string> {
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<void> {
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<void> {
await this.sqlProjService.updateSqlCmdVariable(this.projectFilePath, name, defaultValue);
const result = await this.sqlProjService.updateSqlCmdVariable(this.projectFilePath, name, defaultValue);
this.throwIfFailed(result);
await this.readSqlCmdVariables();
}

View File

@@ -20,7 +20,7 @@ export interface PublishProfile {
serverName: string;
connectionId: string;
connection: string;
sqlCmdVariables: Record<string, string>;
sqlCmdVariables: Map<string, string>;
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<string, string>, deploymentOptions?: mssql.DeploymentOptions): Promise<void> {
export async function savePublishProfile(profilePath: string, databaseName: string, connectionString: string, sqlCommandVariableValues?: Map<string, string>, deploymentOptions?: mssql.DeploymentOptions): Promise<void> {
const dacFxService = await utils.getDacFxService();
await dacFxService.savePublishProfile(profilePath, databaseName, connectionString, sqlCommandVariableValues, deploymentOptions);
}

View File

@@ -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<string, string>) {
constructor(projectNodeName: string, sqlprojUri: vscode.Uri, sqlCmdVariables: Map<string, string>) {
super(vscode.Uri.file(path.join(projectNodeName, constants.sqlcmdVariablesNodeName)), sqlprojUri);
this.construct(sqlCmdVariables);
}
private construct(sqlCmdVariables: Record<string, string>) {
private construct(sqlCmdVariables: Map<string, string>) {
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));
}
}

View File

@@ -248,7 +248,7 @@ declare module 'sqldbproj' {
/**
* SqlCmd variables and their values
*/
readonly sqlCmdVariables: Record<string, string>;
readonly sqlCmdVariables: Map<string, string>;
/**
* Pre-deployment scripts in this project

View File

@@ -13,14 +13,14 @@ export let newSdkSqlProjectTemplate: string;
// Object maps
let scriptTypeMap: Record<string, ProjectScriptType> = {};
let scriptTypeMap: Map<string, ProjectScriptType> = 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, string>): string {
export function macroExpansion(template: string, macroDict: Map<string, string>): 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 = [];
}

View File

@@ -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
}

View File

@@ -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<void> {
@@ -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<void> {
@@ -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<void> {
@@ -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('<SqlCmdVariable Include="test3Db">');
@@ -850,7 +850,7 @@ describe('Project: add SQLCMD Variables', function (): void {
it('Should update .sqlproj with new sqlcmd variables', async function (): Promise<void> {
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');
});
});

View File

@@ -303,7 +303,7 @@ describe('ProjectsController', function (): void {
});
it('Should exclude nested ProjectEntry from node', async function (): Promise<void> {
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(<any>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(<any>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<void> {
@@ -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<void> {
@@ -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');
});
});
});

View File

@@ -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<void> {

View File

@@ -56,13 +56,13 @@ export class MockDacFxService implements mssql.IDacFxService {
public importBacpac(_packageFilePath: string, _databaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
public extractDacpac(_databaseName: string, _packageFilePath: string, _applicationName: string, _applicationVersion: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> { 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<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
public deployDacpac(_packageFilePath: string, _targetDatabaseName: string, _upgradeExisting: boolean, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Record<string, string>, _deploymentOptions?: mssql.DeploymentOptions): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
public generateDeployScript(_packageFilePath: string, _targetDatabaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Record<string, string>, _deploymentOptions?: mssql.DeploymentOptions): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
public deployDacpac(_packageFilePath: string, _targetDatabaseName: string, _upgradeExisting: boolean, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Map<string, string>, _deploymentOptions?: mssql.DeploymentOptions): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
public generateDeployScript(_packageFilePath: string, _targetDatabaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode, _sqlCommandVariableValues?: Map<string, string>, _deploymentOptions?: mssql.DeploymentOptions): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
public generateDeployPlan(_packageFilePath: string, _targetDatabaseName: string, _ownerUri: string, _taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.GenerateDeployPlanResult> { return Promise.resolve(mockDacFxResult); }
public getOptionsFromProfile(_profilePath: string): Thenable<mssql.DacFxOptionsResult> { return Promise.resolve(mockDacFxOptionsResult); }
public validateStreamingJob(_packageFilePath: string, _createStreamingJobTsql: string): Thenable<mssql.ValidateStreamingJobResult> { return Promise.resolve(mockDacFxResult); }
public parseTSqlScript(_filePath: string, _databaseSchemaProvider: string): Thenable<mssql.ParseTSqlScriptResult> { return Promise.resolve({ containsCreateTableStatement: true }); }
public savePublishProfile(_profilePath: string, _databaseName: string, _connectionString: string, _sqlCommandVariableValues?: Record<string, string>, _deploymentOptions?: mssql.DeploymentOptions): Thenable<azdata.ResultStatus> { return Promise.resolve(mockSavePublishResult); }
public savePublishProfile(_profilePath: string, _databaseName: string, _connectionString: string, _sqlCommandVariableValues?: Map<string, string>, _deploymentOptions?: mssql.DeploymentOptions): Thenable<azdata.ResultStatus> { return Promise.resolve(mockSavePublishResult); }
}
export function createContext(): TestContext {

View File

@@ -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<string> {
folderPath = folderPath ?? path.join(await generateTestFolderPath(test), 'TestProject');
const macroDict: Record<string, string> = {
'PROJECT_DSP': constants.defaultDSP
};
const macroDict: Map<string, string> = new Map([['PROJECT_DSP', constants.defaultDSP]]);
contents = templates.macroExpansion(contents, macroDict);
return await createTestFile(test, contents, 'TestProject.sqlproj', folderPath);
}