diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index d82ad7f4c0..7c8985166f 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -47,6 +47,7 @@ import { ILocalDbDeployProfile, ISqlDbDeployProfile } from '../models/deploy/dep import { EntryType, FileProjectEntry, IDatabaseReferenceProjectEntry, SqlProjectReferenceProjectEntry } from '../models/projectEntry'; import { UpdateProjectAction, UpdateProjectDataModel } from '../models/api/updateProject'; import { AzureSqlClient } from '../models/deploy/azureSqlClient'; +import { ConnectionService } from '../models/connections/connectionService'; const maxTableLength = 10; @@ -74,6 +75,7 @@ export class ProjectsController { private buildInfo: DashboardData[] = []; private publishInfo: PublishData[] = []; private deployService: DeployService; + private connectionService: ConnectionService; private azureSqlClient: AzureSqlClient; private autorestHelper: AutorestHelper; @@ -84,6 +86,7 @@ export class ProjectsController { this.buildHelper = new BuildHelper(); this.azureSqlClient = new AzureSqlClient(); this.deployService = new DeployService(this.azureSqlClient, this._outputChannel); + this.connectionService = new ConnectionService(this._outputChannel); this.autorestHelper = new AutorestHelper(this._outputChannel); } @@ -293,7 +296,7 @@ export class ProjectsController { if (deployProfile.sqlDbSetting) { // Connecting to the deployed db to add the profile to connection viewlet - await this.deployService.getConnection(deployProfile.sqlDbSetting, true, deployProfile.sqlDbSetting.dbName); + await this.connectionService.getConnection(deployProfile.sqlDbSetting, true, deployProfile.sqlDbSetting.dbName); } void vscode.window.showInformationMessage(constants.publishProjectSucceed); } else { @@ -335,7 +338,7 @@ export class ProjectsController { const publishResult = await this.publishOrScriptProject(project, deployProfile.deploySettings, true); if (publishResult && publishResult.success) { if (deployProfile.localDbSetting) { - await this.deployService.getConnection(deployProfile.localDbSetting, true, deployProfile.localDbSetting.dbName); + await this.connectionService.getConnection(deployProfile.localDbSetting, true, deployProfile.localDbSetting.dbName); } void vscode.window.showInformationMessage(constants.publishProjectSucceed); } else { diff --git a/extensions/sql-database-projects/src/models/connections/connectionService.ts b/extensions/sql-database-projects/src/models/connections/connectionService.ts new file mode 100644 index 0000000000..af82846f49 --- /dev/null +++ b/extensions/sql-database-projects/src/models/connections/connectionService.ts @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as constants from '../../common/constants'; +import * as utils from '../../common/utils'; +import * as vscode from 'vscode'; +import { ConnectionResult } from 'azdata'; +import { IFireWallRuleError } from 'vscode-mssql'; + +export interface ISqlConnectionProperties { + tenantId?: string, + accountId?: string + serverName: string, + userName: string, + password: string, + port: number, + dbName: string, + profileName?: string, + connectionRetryTimeout?: number +} + +/** + * Includes methods to open connections and interact with connection views + */ +export class ConnectionService { + + constructor(private _outputChannel: vscode.OutputChannel) { + } + + private defaultSqlRetryTimeoutInSec: number = 10; + private defaultSqlNumberOfRetries: number = 3; + + /** + * Connects to a database + * @param profile connection profile + * @param saveConnectionAndPassword if true, connection will be saved in the connection view + * @param database database name + * @returns + */ + private async connectToDatabase(profile: ISqlConnectionProperties, saveConnectionAndPassword: boolean, database: string): Promise { + const azdataApi = utils.getAzdataApi(); + const vscodeMssqlApi = azdataApi ? undefined : await utils.getVscodeMssqlApi(); + if (azdataApi) { + const connectionProfile = { + password: profile.password, + serverName: `${profile.serverName},${profile.port}`, + database: database, + savePassword: saveConnectionAndPassword, + userName: profile.userName, + providerName: 'MSSQL', + saveProfile: false, + id: '', + connectionName: profile.profileName, + options: [], + authenticationType: 'SqlLogin' + }; + return await azdataApi.connection.connect(connectionProfile, saveConnectionAndPassword, false); + } else if (vscodeMssqlApi) { + const connectionProfile = { + password: profile.password, + server: `${profile.serverName}`, + port: profile.port, + database: database, + savePassword: saveConnectionAndPassword, + user: profile.userName, + authenticationType: 'SqlLogin', + encrypt: false, + connectTimeout: 30, + applicationName: 'SQL Database Project', + accountId: profile.accountId, + azureAccountToken: undefined, + applicationIntent: undefined, + attachDbFilename: undefined, + connectRetryCount: undefined, + connectRetryInterval: undefined, + connectionString: undefined, + currentLanguage: undefined, + email: undefined, + failoverPartner: undefined, + loadBalanceTimeout: undefined, + maxPoolSize: undefined, + minPoolSize: undefined, + multiSubnetFailover: undefined, + multipleActiveResultSets: undefined, + packetSize: undefined, + persistSecurityInfo: undefined, + pooling: undefined, + replication: undefined, + trustServerCertificate: undefined, + typeSystemVersion: undefined, + workstationId: undefined, + profileName: profile.profileName, + expiresOn: undefined, + tenantId: profile.tenantId + }; + let connectionUrl = ''; + try { + connectionUrl = await vscodeMssqlApi.connect(connectionProfile, saveConnectionAndPassword); + } catch (err) { + const firewallRuleError = err; + if (firewallRuleError?.connectionUri) { + await vscodeMssqlApi.promptForFirewallRule(err.connectionUri, connectionProfile); + } else { + throw err; + } + } + + // If connected successfully and saved the connection in the view, open the connection view + if (saveConnectionAndPassword && connectionUrl) { + await vscode.commands.executeCommand('objectExplorer.focus'); + } + return connectionUrl; + } else { + return undefined; + } + } + + /** + * Validates the connection result. If using azdata API, verifies connection was successful and connection id is returns + * If using vscode API, verifies the connection url is returns + * @param connection connection result or connection Id + * @returns validation result + */ + private async validateConnection(connection: ConnectionResult | string | undefined): Promise { + const azdataApi = utils.getAzdataApi(); + if (!connection) { + return { validated: false, errorMessage: constants.connectionFailedError('No result returned') }; + } else if (azdataApi) { + const connectionResult = connection; + if (connectionResult) { + const connected = connectionResult !== undefined && connectionResult.connected && connectionResult.connectionId !== undefined; + return { validated: connected, errorMessage: connected ? '' : constants.connectionFailedError(connectionResult?.errorMessage!) }; + } else { + return { validated: false, errorMessage: constants.connectionFailedError('') }; + } + } else { + return { validated: connection !== undefined, errorMessage: constants.connectionFailedError('') }; + } + } + + /** + * Formats connection result to string to be able to add to log + * @param connection connection result or connection Id + * @returns formatted connection result + */ + private async formatConnectionResult(connection: ConnectionResult | string | undefined): Promise { + const azdataApi = utils.getAzdataApi(); + const connectionResult = connection !== undefined && azdataApi ? connection : undefined; + return connectionResult?.connected ? connectionResult.connectionId! : connection; + } + + /** + * Opens a connections and returns the connection id + * @param profile connection profile + * @param saveConnectionAndPassword is set to true the connection will be saved in the connection view + * @param database database name + * @returns connection id + */ + public async getConnection(profile: ISqlConnectionProperties, saveConnectionAndPassword: boolean, database: string): Promise { + const azdataApi = utils.getAzdataApi(); + let connection = await utils.retry( + constants.connectingToSqlServerMessage, + async () => { + return await this.connectToDatabase(profile, saveConnectionAndPassword, database); + }, + this.validateConnection, + this.formatConnectionResult, + this._outputChannel, + this.defaultSqlNumberOfRetries, profile.connectionRetryTimeout || this.defaultSqlRetryTimeoutInSec); + + if (connection) { + const connectionResult = connection; + if (azdataApi) { + utils.throwIfNotConnected(connectionResult); + return azdataApi.connection.getUriForConnection(connectionResult.connectionId!); + } else { + return connection; + } + } + + return undefined; + } +} diff --git a/extensions/sql-database-projects/src/models/deploy/deployProfile.ts b/extensions/sql-database-projects/src/models/deploy/deployProfile.ts index 70511603c5..80dfc77fac 100644 --- a/extensions/sql-database-projects/src/models/deploy/deployProfile.ts +++ b/extensions/sql-database-projects/src/models/deploy/deployProfile.ts @@ -6,6 +6,7 @@ import { IDeploySettings } from '../IDeploySettings'; import type * as azdataType from 'azdata'; import { IAzureAccountSession } from 'vscode-mssql'; +import { ISqlConnectionProperties } from '../connections/connectionService'; export enum AppSettingType { None, @@ -38,17 +39,7 @@ export interface ILocalDbSetting extends ISqlConnectionProperties { dockerBaseImageEula: string, } -export interface ISqlConnectionProperties { - tenantId?: string, - accountId?: string - serverName: string, - userName: string, - password: string, - port: number, - dbName: string, - profileName?: string, - connectionRetryTimeout?: number -} + export interface DockerImageInfo { name: string, diff --git a/extensions/sql-database-projects/src/models/deploy/deployService.ts b/extensions/sql-database-projects/src/models/deploy/deployService.ts index 70eefe3f4c..8b018b1d72 100644 --- a/extensions/sql-database-projects/src/models/deploy/deployService.ts +++ b/extensions/sql-database-projects/src/models/deploy/deployService.ts @@ -3,18 +3,15 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AppSettingType, IDeployAppIntegrationProfile, ILocalDbDeployProfile, ILocalDbSetting, ISqlConnectionProperties, ISqlDbDeployProfile } from './deployProfile'; +import { ILocalDbDeployProfile, ILocalDbSetting, ISqlDbDeployProfile } from './deployProfile'; import * as UUID from 'vscode-languageclient/lib/utils/uuid'; import { Project } from '../project'; import * as constants from '../../common/constants'; import * as utils from '../../common/utils'; -import * as fse from 'fs-extra'; import * as vscode from 'vscode'; -import { ConnectionResult } from 'azdata'; -import * as templates from '../../templates/templates'; import { ShellExecutionHelper } from '../../tools/shellExecutionHelper'; import { AzureSqlClient } from './azureSqlClient'; -import { IFireWallRuleError } from 'vscode-mssql'; +import { ConnectionService } from '../connections/connectionService'; interface DockerImageSpec { label: string; @@ -25,73 +22,11 @@ export class DeployService { constructor(private _azureSqlClient = new AzureSqlClient(), private _outputChannel: vscode.OutputChannel, shellExecutionHelper: ShellExecutionHelper | undefined = undefined) { this._shellExecutionHelper = shellExecutionHelper ?? new ShellExecutionHelper(this._outputChannel); + this._connectionService = new ConnectionService(this._outputChannel); } private _shellExecutionHelper: ShellExecutionHelper; - private DefaultSqlRetryTimeoutInSec: number = 10; - private DefaultSqlNumberOfRetries: number = 3; - - private createConnectionStringTemplate(runtime: string | undefined): string { - switch (runtime?.toLocaleLowerCase()) { - case 'dotnet': - return constants.defaultConnectionStringTemplate; - break; - // TODO: add connection strings for other languages - default: - break; - } - return ''; - } - - private findAppRuntime(profile: IDeployAppIntegrationProfile, appSettingContent: any): string | undefined { - switch (profile.appSettingType) { - case AppSettingType.AzureFunction: - return appSettingContent?.Values['FUNCTIONS_WORKER_RUNTIME']; - default: - } - return undefined; - } - - public async updateAppSettings(profile: IDeployAppIntegrationProfile, deployProfile: ILocalDbDeployProfile | undefined): Promise { - // Update app settings - // - if (!profile.appSettingFile) { - return; - } - this.logToOutput(constants.deployAppSettingUpdating(profile.appSettingFile)); - - // TODO: handle parsing errors - let content = JSON.parse(fse.readFileSync(profile.appSettingFile, 'utf8')); - if (content && content.Values) { - let connectionString: string | undefined = ''; - if (deployProfile && deployProfile.localDbSetting) { - // Find the runtime and generate the connection string for the runtime - // - const runtime = this.findAppRuntime(profile, content); - let connectionStringTemplate = this.createConnectionStringTemplate(runtime); - const macroDict: Record = { - 'SERVER': deployProfile?.localDbSetting?.serverName || '', - 'PORT': deployProfile?.localDbSetting?.port?.toString() || '', - 'USER': deployProfile?.localDbSetting?.userName || '', - 'SA_PASSWORD': deployProfile?.localDbSetting?.password || '', - 'DATABASE': deployProfile?.localDbSetting?.dbName || '', - }; - - connectionString = templates.macroExpansion(connectionStringTemplate, macroDict); - } else if (deployProfile?.deploySettings?.connectionUri) { - connectionString = await this.getConnectionString(deployProfile?.deploySettings?.connectionUri); - } - - if (connectionString && profile.envVariableName) { - content.Values[profile.envVariableName] = connectionString; - fse.writeFileSync(profile.appSettingFile, JSON.stringify(content, undefined, 4)); - this.logToOutput(`app setting '${profile.appSettingFile}' has been updated. env variable name: ${profile.envVariableName} connection String: ${connectionString}`); - - } else { - this.logToOutput(constants.deployAppSettingUpdateFailed(profile.appSettingFile)); - } - } - } + private _connectionService: ConnectionService; private async verifyDocker(): Promise { try { @@ -149,7 +84,7 @@ export class DeployService { this.logToOutput(constants.azureSqlServerCreated(profile?.sqlDbSetting?.serverName)); // Connect to the server - return await this.getConnection(profile.sqlDbSetting, false, constants.master); + return await this._connectionService.getConnection(profile.sqlDbSetting, false, constants.master); } return undefined; } @@ -209,7 +144,7 @@ export class DeployService { if (runningDockerId) { this.logToOutput(constants.dockerContainerCreatedMessage(runningDockerId)); - return await this.getConnection(profile.localDbSetting, false, 'master'); + return await this._connectionService.getConnection(profile.localDbSetting, false, 'master'); } else { this.logToOutput(constants.dockerContainerFailedToRunErrorMessage); @@ -235,145 +170,6 @@ export class DeployService { return await this.executeCommand(`docker ps -q -a --filter label=${dockerImageSpec.label} -q`); } - private async getConnectionString(connectionUri: string): Promise { - const azdataApi = utils.getAzdataApi(); - if (azdataApi) { - const connection = await azdataApi.connection.getConnection(connectionUri); - if (connection) { - return await azdataApi.connection.getConnectionString(connection.connectionId, true); - } - } - // TODO: vscode connections string - - return undefined; - - } - - // Connects to a database - private async connectToDatabase(profile: ISqlConnectionProperties, saveConnectionAndPassword: boolean, database: string): Promise { - const azdataApi = utils.getAzdataApi(); - const vscodeMssqlApi = azdataApi ? undefined : await utils.getVscodeMssqlApi(); - if (azdataApi) { - const connectionProfile = { - password: profile.password, - serverName: `${profile.serverName},${profile.port}`, - database: database, - savePassword: saveConnectionAndPassword, - userName: profile.userName, - providerName: 'MSSQL', - saveProfile: false, - id: '', - connectionName: profile.profileName, - options: [], - authenticationType: 'SqlLogin' - }; - return await azdataApi.connection.connect(connectionProfile, saveConnectionAndPassword, false); - } else if (vscodeMssqlApi) { - const connectionProfile = { - password: profile.password, - server: `${profile.serverName}`, - port: profile.port, - database: database, - savePassword: saveConnectionAndPassword, - user: profile.userName, - authenticationType: 'SqlLogin', - encrypt: false, - connectTimeout: 30, - applicationName: 'SQL Database Project', - accountId: profile.accountId, - azureAccountToken: undefined, - applicationIntent: undefined, - attachDbFilename: undefined, - connectRetryCount: undefined, - connectRetryInterval: undefined, - connectionString: undefined, - currentLanguage: undefined, - email: undefined, - failoverPartner: undefined, - loadBalanceTimeout: undefined, - maxPoolSize: undefined, - minPoolSize: undefined, - multiSubnetFailover: undefined, - multipleActiveResultSets: undefined, - packetSize: undefined, - persistSecurityInfo: undefined, - pooling: undefined, - replication: undefined, - trustServerCertificate: undefined, - typeSystemVersion: undefined, - workstationId: undefined, - profileName: profile.profileName, - expiresOn: undefined, - tenantId: profile.tenantId - }; - let connectionUrl = ''; - try { - connectionUrl = await vscodeMssqlApi.connect(connectionProfile, saveConnectionAndPassword); - } catch (err) { - const firewallRuleError = err; - if (firewallRuleError?.connectionUri) { - await vscodeMssqlApi.promptForFirewallRule(err.connectionUri, connectionProfile); - } else { - throw err; - } - } - return connectionUrl; - } else { - return undefined; - } - } - - // Validates the connection result. If using azdata API, verifies connection was successful and connection id is returns - // If using vscode API, verifies the connection url is returns - private async validateConnection(connection: ConnectionResult | string | undefined): Promise { - const azdataApi = utils.getAzdataApi(); - if (!connection) { - return { validated: false, errorMessage: constants.connectionFailedError('No result returned') }; - } else if (azdataApi) { - const connectionResult = connection; - if (connectionResult) { - const connected = connectionResult !== undefined && connectionResult.connected && connectionResult.connectionId !== undefined; - return { validated: connected, errorMessage: connected ? '' : constants.connectionFailedError(connectionResult?.errorMessage!) }; - } else { - return { validated: false, errorMessage: constants.connectionFailedError('') }; - } - } else { - return { validated: connection !== undefined, errorMessage: constants.connectionFailedError('') }; - } - } - - // Formats connection result to string to be able to add to log - private async formatConnectionResult(connection: ConnectionResult | string | undefined): Promise { - const azdataApi = utils.getAzdataApi(); - const connectionResult = connection !== undefined && azdataApi ? connection : undefined; - return connectionResult?.connected ? connectionResult.connectionId! : connection; - } - - public async getConnection(profile: ISqlConnectionProperties, saveConnectionAndPassword: boolean, database: string): Promise { - const azdataApi = utils.getAzdataApi(); - let connection = await utils.retry( - constants.connectingToSqlServerMessage, - async () => { - return await this.connectToDatabase(profile, saveConnectionAndPassword, database); - }, - this.validateConnection, - this.formatConnectionResult, - this._outputChannel, - this.DefaultSqlNumberOfRetries, profile.connectionRetryTimeout || this.DefaultSqlRetryTimeoutInSec); - - if (connection) { - const connectionResult = connection; - if (azdataApi) { - utils.throwIfNotConnected(connectionResult); - return azdataApi.connection.getUriForConnection(connectionResult.connectionId!); - } else { - return connection; - } - } - - return undefined; - } - private async executeTask(taskName: string, task: () => Promise): Promise { const azdataApi = utils.getAzdataApi(); if (azdataApi) { diff --git a/extensions/sql-database-projects/src/test/deploy/deployService.test.ts b/extensions/sql-database-projects/src/test/deploy/deployService.test.ts index 2ec4eb84a7..4ef73f7b26 100644 --- a/extensions/sql-database-projects/src/test/deploy/deployService.test.ts +++ b/extensions/sql-database-projects/src/test/deploy/deployService.test.ts @@ -11,14 +11,13 @@ import { DeployService } from '../../models/deploy/deployService'; import { Project } from '../../models/project'; import * as vscode from 'vscode'; import * as azdata from 'azdata'; -import { AppSettingType, ILocalDbDeployProfile, ISqlDbDeployProfile } from '../../models/deploy/deployProfile'; +import { ILocalDbDeployProfile, ISqlDbDeployProfile } from '../../models/deploy/deployProfile'; import * as UUID from 'vscode-languageclient/lib/utils/uuid'; -import * as fse from 'fs-extra'; -import * as path from 'path'; import * as constants from '../../common/constants'; import { ShellExecutionHelper } from '../../tools/shellExecutionHelper'; import * as TypeMoq from 'typemoq'; import { AzureSqlClient } from '../../models/deploy/azureSqlClient'; +import { ConnectionService } from '../../models/connections/connectionService'; export interface TestContext { outputChannel: vscode.OutputChannel; @@ -140,125 +139,17 @@ describe('deploy service', function (): void { const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper); shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id')); - const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object); + const connectionService = new ConnectionService(testContext.outputChannel); let connectionStub = sandbox.stub(azdata.connection, 'connect'); connectionStub.onFirstCall().returns(Promise.resolve(mockFailedConnectionResult)); connectionStub.onSecondCall().returns(Promise.resolve(mockConnectionResult)); sandbox.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('connection')); sandbox.stub(azdata.tasks, 'startBackgroundOperation').callThrough(); - let connection = await deployService.getConnection(localDbSettings, false, 'master'); + let connection = await connectionService.getConnection(localDbSettings, false, 'master'); should(connection).equals('connection'); }); - it('Should update app settings successfully', async function (): Promise { - const testContext = createContext(); - const projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline); - const project1 = await Project.openProject(vscode.Uri.file(projFilePath).fsPath); - const jsondData = - { - IsEncrypted: false, - Values: { - AzureWebJobsStorage: 'UseDevelopmentStorage=true', - FUNCTIONS_WORKER_RUNTIME: 'dotnet' - } - }; - let settingContent = JSON.stringify(jsondData, undefined, 4); - const expected = - { - IsEncrypted: false, - Values: { - AzureWebJobsStorage: 'UseDevelopmentStorage=true', - FUNCTIONS_WORKER_RUNTIME: 'dotnet', - SQLConnectionString: 'Data Source=localhost,1433;Initial Catalog=test;User id=sa;Password=PLACEHOLDER;' - } - }; - const filePath = path.join(project1.projectFolderPath, 'local.settings.json'); - await fse.writeFile(filePath, settingContent); - - const deployProfile: ILocalDbDeployProfile = { - localDbSetting: { - dbName: 'test', - password: 'PLACEHOLDER', - port: 1433, - serverName: 'localhost', - userName: 'sa', - dockerBaseImage: 'image', - dockerBaseImageEula: '' - } - }; - - const appInteg = { - appSettingType: AppSettingType.AzureFunction, - appSettingFile: filePath, - deploySettings: undefined, - envVariableName: 'SQLConnectionString' - }; - - const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper); - shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(), - undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id')); - const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object); - - await deployService.updateAppSettings(appInteg, deployProfile); - let newContent = JSON.parse(fse.readFileSync(filePath, 'utf8')); - should(newContent).deepEqual(expected); - - }); - - it('Should update app settings using connection uri if there are no local settings', async function (): Promise { - const testContext = createContext(); - const projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline); - const project1 = await Project.openProject(vscode.Uri.file(projFilePath).fsPath); - const jsondData = - { - IsEncrypted: false, - Values: { - AzureWebJobsStorage: 'UseDevelopmentStorage=true', - FUNCTIONS_WORKER_RUNTIME: 'dotnet' - } - }; - let settingContent = JSON.stringify(jsondData, undefined, 4); - const expected = - { - IsEncrypted: false, - Values: { - AzureWebJobsStorage: 'UseDevelopmentStorage=true', - FUNCTIONS_WORKER_RUNTIME: 'dotnet', - SQLConnectionString: 'connectionString' - } - }; - const filePath = path.join(project1.projectFolderPath, 'local.settings.json'); - await fse.writeFile(filePath, settingContent); - - const deployProfile: ILocalDbDeployProfile = { - - deploySettings: { - connectionUri: 'connection', - databaseName: 'test', - serverName: 'test' - }, - localDbSetting: undefined - }; - - const appInteg = { - appSettingType: AppSettingType.AzureFunction, - appSettingFile: filePath, - envVariableName: 'SQLConnectionString', - }; - const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper); - shellExecutionHelper.setup(x => x.runStreamedCommand(TypeMoq.It.isAny(), - undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve('id')); - const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel, shellExecutionHelper.object); - let connection = new azdata.connection.ConnectionProfile(); - sandbox.stub(azdata.connection, 'getConnection').returns(Promise.resolve(connection)); - - sandbox.stub(azdata.connection, 'getConnectionString').returns(Promise.resolve('connectionString')); - await deployService.updateAppSettings(appInteg, deployProfile); - let newContent = JSON.parse(fse.readFileSync(filePath, 'utf8')); - should(newContent).deepEqual(expected); - }); - it('Should clean a list of docker images successfully', async function (): Promise { const testContext = createContext(); const shellExecutionHelper = TypeMoq.Mock.ofType(ShellExecutionHelper);