SQL Project - deploy to docker publish option (#17050)

SQL Project - deploy to docker publish option
This commit is contained in:
Leila Lali
2021-09-13 14:12:53 -07:00
committed by GitHub
parent 90bb9c3c55
commit 4912faa966
13 changed files with 158 additions and 143 deletions

View File

@@ -114,11 +114,6 @@
"title": "%sqlDatabaseProjects.publish%", "title": "%sqlDatabaseProjects.publish%",
"category": "%sqlDatabaseProjects.displayName%" "category": "%sqlDatabaseProjects.displayName%"
}, },
{
"command": "sqlDatabaseProjects.deployLocal",
"title": "%sqlDatabaseProjects.deployLocal%",
"category": "%sqlDatabaseProjects.displayName%"
},
{ {
"command": "sqlDatabaseProjects.properties", "command": "sqlDatabaseProjects.properties",
"title": "%sqlDatabaseProjects.properties%", "title": "%sqlDatabaseProjects.properties%",
@@ -222,10 +217,6 @@
"command": "sqlDatabaseProjects.publish", "command": "sqlDatabaseProjects.publish",
"when": "false" "when": "false"
}, },
{
"command": "sqlDatabaseProjects.deployLocal",
"when": "false"
},
{ {
"command": "sqlDatabaseProjects.properties", "command": "sqlDatabaseProjects.properties",
"when": "false" "when": "false"
@@ -277,11 +268,6 @@
"when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project", "when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project",
"group": "1_dbProjectsFirst@2" "group": "1_dbProjectsFirst@2"
}, },
{
"command": "sqlDatabaseProjects.deployLocal",
"when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project && !azdataAvailable",
"group": "1_dbProjectsFirst@2"
},
{ {
"command": "sqlDatabaseProjects.schemaCompare", "command": "sqlDatabaseProjects.schemaCompare",
"when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project && azdataAvailable", "when": "view == dataworkspace.views.main && viewItem == databaseProject.itemType.project && azdataAvailable",
@@ -400,7 +386,6 @@
"@microsoft/ads-extension-telemetry": "^1.1.5", "@microsoft/ads-extension-telemetry": "^1.1.5",
"fast-glob": "^3.1.0", "fast-glob": "^3.1.0",
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",
"generate-password": "^1.6.0",
"jsonc-parser": "^2.3.1", "jsonc-parser": "^2.3.1",
"promisify-child-process": "^3.1.1", "promisify-child-process": "^3.1.1",
"vscode-languageclient": "^5.3.0-next.1", "vscode-languageclient": "^5.3.0-next.1",

View File

@@ -8,7 +8,6 @@
"sqlDatabaseProjects.close": "Close Database Project", "sqlDatabaseProjects.close": "Close Database Project",
"sqlDatabaseProjects.build": "Build", "sqlDatabaseProjects.build": "Build",
"sqlDatabaseProjects.publish": "Publish", "sqlDatabaseProjects.publish": "Publish",
"sqlDatabaseProjects.deployLocal": "Deploy",
"sqlDatabaseProjects.createProjectFromDatabase": "Create Project From Database", "sqlDatabaseProjects.createProjectFromDatabase": "Create Project From Database",
"sqlDatabaseProjects.properties": "Properties", "sqlDatabaseProjects.properties": "Properties",
"sqlDatabaseProjects.schemaCompare": "Schema Compare", "sqlDatabaseProjects.schemaCompare": "Schema Compare",

View File

@@ -128,13 +128,13 @@ export const done = localize('done', "Done");
export const nameMustNotBeEmpty = localize('nameMustNotBeEmpty', "Name must not be empty"); export const nameMustNotBeEmpty = localize('nameMustNotBeEmpty', "Name must not be empty");
// Deploy // Deploy
export const selectDeployOption = localize('selectDeployOption', "Select where to deploy the project to"); export const selectPublishOption = localize('selectPublishOption', "Select where to publish the project to");
export const deployToExistingServer = localize('deployToExistingServer', "Deploy to existing server"); export const publishToExistingServer = localize('publishToExistingServer', "Publish to existing server");
export const deployToDockerContainer = localize('deployToDockerContainer', "Deploy to docker container"); export const publishToDockerContainer = localize('publishToDockerContainer', "Publish to docker container");
export const enterPortNumber = localize('enterPortNumber', "Enter port number or press enter to use the default value"); export const enterPortNumber = localize('enterPortNumber', "Enter port number or press enter to use the default value");
export const enterConnectionStringEnvName = localize('enterConnectionStringEnvName', "Enter connection string environment variable name"); export const enterConnectionStringEnvName = localize('enterConnectionStringEnvName', "Enter connection string environment variable name");
export const enterConnectionStringTemplate = localize('enterConnectionStringTemplate', "Enter connection string template"); export const enterConnectionStringTemplate = localize('enterConnectionStringTemplate', "Enter connection string template");
export const enterPassword = localize('enterPassword', "Enter password or press enter to use the generated password"); export const enterPassword = localize('enterPassword', "Enter password");
export const portMustBeNumber = localize('portMustNotBeNumber', "Port must a be number"); export const portMustBeNumber = localize('portMustNotBeNumber', "Port must a be number");
export const valueCannotBeEmpty = localize('valueCannotBeEmpty', "Value cannot be empty"); export const valueCannotBeEmpty = localize('valueCannotBeEmpty', "Value cannot be empty");
export const dockerImageLabelPrefix = 'source=sqldbproject'; export const dockerImageLabelPrefix = 'source=sqldbproject';

View File

@@ -505,3 +505,15 @@ export async function getAllProjectsInFolder(folder: vscode.Uri, projectExtensio
// glob will return an array of file paths with forward slashes, so they need to be converted back if on windows // glob will return an array of file paths with forward slashes, so they need to be converted back if on windows
return (await glob(projFilter)).map(p => vscode.Uri.file(path.resolve(p))); return (await glob(projFilter)).map(p => vscode.Uri.file(path.resolve(p)));
} }
export function validateSqlServerPortNumber(port: string | undefined): boolean {
if (!port) {
return false;
}
const valueAsNum = +port;
return !isNaN(valueAsNum) && valueAsNum > 0 && valueAsNum < 65535;
}
export function isEmptyString(password: string | undefined): boolean {
return password === undefined || password === '';
}

View File

@@ -55,7 +55,6 @@ export default class MainController implements vscode.Disposable {
vscode.commands.registerCommand('sqlDatabaseProjects.build', async (node: WorkspaceTreeItem) => { return this.projectsController.buildProject(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.build', async (node: WorkspaceTreeItem) => { return this.projectsController.buildProject(node); });
vscode.commands.registerCommand('sqlDatabaseProjects.publish', async (node: WorkspaceTreeItem) => { this.projectsController.publishProject(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.publish', async (node: WorkspaceTreeItem) => { this.projectsController.publishProject(node); });
vscode.commands.registerCommand('sqlDatabaseProjects.deployLocal', async (node: WorkspaceTreeItem) => { return this.projectsController.deployProject(node); });
vscode.commands.registerCommand('sqlDatabaseProjects.schemaCompare', async (node: WorkspaceTreeItem) => { return this.projectsController.schemaCompare(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.schemaCompare', async (node: WorkspaceTreeItem) => { return this.projectsController.schemaCompare(node); });
vscode.commands.registerCommand('sqlDatabaseProjects.createProjectFromDatabase', async (context: azdataType.IConnectionProfile | vscodeMssql.ITreeNodeInfo | undefined) => { return this.projectsController.createProjectFromDatabase(context); }); vscode.commands.registerCommand('sqlDatabaseProjects.createProjectFromDatabase', async (context: azdataType.IConnectionProfile | vscodeMssql.ITreeNodeInfo | undefined) => { return this.projectsController.createProjectFromDatabase(context); });

View File

@@ -35,7 +35,7 @@ import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/t
import { IconPathHelper } from '../common/iconHelper'; import { IconPathHelper } from '../common/iconHelper';
import { DashboardData, PublishData, Status } from '../models/dashboardData/dashboardData'; import { DashboardData, PublishData, Status } from '../models/dashboardData/dashboardData';
import { launchPublishDatabaseQuickpick } from '../dialogs/publishDatabaseQuickpick'; import { launchPublishDatabaseQuickpick } from '../dialogs/publishDatabaseQuickpick';
import { launchDeployDatabaseQuickpick } from '../dialogs/deployDatabaseQuickpick'; import { launchPublishToDockerContainerQuickpick } from '../dialogs/deployDatabaseQuickpick';
import { DeployService } from '../models/deploy/deployService'; import { DeployService } from '../models/deploy/deployService';
import { SqlTargetPlatform } from 'sqldbproj'; import { SqlTargetPlatform } from 'sqldbproj';
import { createNewProjectFromDatabaseWithQuickpick } from '../dialogs/createProjectFromDatabaseQuickpick'; import { createNewProjectFromDatabaseWithQuickpick } from '../dialogs/createProjectFromDatabaseQuickpick';
@@ -256,13 +256,13 @@ export class ProjectsController {
} }
/** /**
* Deploys a project * Publishes a project to docker container
* @param treeNode a treeItem in a project's hierarchy, to be used to obtain a Project * @param treeNode a treeItem in a project's hierarchy, to be used to obtain a Project
*/ */
public async deployProject(context: Project | dataworkspace.WorkspaceTreeItem): Promise<void> { public async publishToDockerContainer(context: Project | dataworkspace.WorkspaceTreeItem): Promise<void> {
const project: Project = this.getProjectFromContext(context); const project: Project = this.getProjectFromContext(context);
try { try {
let deployProfile = await launchDeployDatabaseQuickpick(project); let deployProfile = await launchPublishToDockerContainerQuickpick(project);
if (deployProfile && deployProfile.deploySettings) { if (deployProfile && deployProfile.deploySettings) {
let connectionUri: string | undefined; let connectionUri: string | undefined;
if (deployProfile.localDbSetting) { if (deployProfile.localDbSetting) {
@@ -274,10 +274,6 @@ export class ProjectsController {
if (deployProfile.deploySettings.connectionUri) { if (deployProfile.deploySettings.connectionUri) {
const publishResult = await this.publishOrScriptProject(project, deployProfile.deploySettings, true); const publishResult = await this.publishOrScriptProject(project, deployProfile.deploySettings, true);
if (publishResult && publishResult.success) { if (publishResult && publishResult.success) {
// Update app settings if requested by user
//
await this.deployService.updateAppSettings(deployProfile);
if (deployProfile.localDbSetting) { if (deployProfile.localDbSetting) {
await this.deployService.getConnection(deployProfile.localDbSetting, true, deployProfile.localDbSetting.dbName); await this.deployService.getConnection(deployProfile.localDbSetting, true, deployProfile.localDbSetting.dbName);
} }

View File

@@ -5,9 +5,9 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as constants from '../common/constants'; import * as constants from '../common/constants';
import { AppSettingType, IDeployProfile, ILocalDbSetting } from '../models/deploy/deployProfile'; import * as utils from '../common/utils';
import { AppSettingType, IDeployAppIntegrationProfile, IDeployProfile, ILocalDbSetting } from '../models/deploy/deployProfile';
import { Project } from '../models/project'; import { Project } from '../models/project';
import * as generator from 'generate-password';
import { getPublishDatabaseSettings } from './publishDatabaseQuickpick'; import { getPublishDatabaseSettings } from './publishDatabaseQuickpick';
import * as path from 'path'; import * as path from 'path';
import * as fse from 'fs-extra'; import * as fse from 'fs-extra';
@@ -15,73 +15,7 @@ import * as fse from 'fs-extra';
/** /**
* Create flow for Deploying a database using only VS Code-native APIs such as QuickPick * Create flow for Deploying a database using only VS Code-native APIs such as QuickPick
*/ */
export async function launchDeployDatabaseQuickpick(project: Project): Promise<IDeployProfile | undefined> { export async function launchDeployAppIntegrationQuickpick(project: Project): Promise<IDeployAppIntegrationProfile | undefined> {
// Show options to user for deploy to existing server or docker
const deployOption = await vscode.window.showQuickPick(
[constants.deployToExistingServer, constants.deployToDockerContainer],
{ title: constants.selectDeployOption, ignoreFocusOut: true });
// Return when user hits escape
if (!deployOption) {
return undefined;
}
let localDbSetting: ILocalDbSetting | undefined;
// Deploy to docker selected
if (deployOption === constants.deployToDockerContainer) {
let portNumber = await vscode.window.showInputBox({
title: constants.enterPortNumber,
ignoreFocusOut: true,
value: constants.defaultPortNumber,
validateInput: input => isNaN(+input) ? constants.portMustBeNumber : undefined
}
);
// Return when user hits escape
if (!portNumber) {
return undefined;
}
let password: string | undefined = generator.generate({
length: 10,
numbers: true,
symbols: true,
lowercase: true,
uppercase: true,
exclude: '`"\'' // Exclude the chars that cannot be included in the password. Some chars can make the command fail in the terminal
});
password = await vscode.window.showInputBox({
title: constants.enterPassword,
ignoreFocusOut: true,
value: password,
password: true
}
);
// Return when user hits escape
if (!password) {
return undefined;
}
localDbSetting = {
serverName: 'localhost',
userName: 'sa',
dbName: project.projectFileName,
password: password,
port: +portNumber,
};
}
let deploySettings = await getPublishDatabaseSettings(project, deployOption !== constants.deployToDockerContainer);
// Return when user hits escape
if (!deploySettings) {
return undefined;
}
// TODO: Ask for SQL CMD Variables or profile
let envVarName: string | undefined = ''; let envVarName: string | undefined = '';
const integrateWithAzureFunctions: boolean = true; //TODO: get value from settings or quickpick const integrateWithAzureFunctions: boolean = true; //TODO: get value from settings or quickpick
@@ -116,7 +50,7 @@ export async function launchDeployDatabaseQuickpick(project: Project): Promise<I
title: constants.enterConnectionStringEnvName, title: constants.enterConnectionStringEnvName,
ignoreFocusOut: true, ignoreFocusOut: true,
value: constants.defaultConnectionStringEnvVarName, value: constants.defaultConnectionStringEnvVarName,
validateInput: input => input === '' ? constants.valueCannotBeEmpty : undefined, validateInput: input => utils.isEmptyString(input) ? constants.valueCannotBeEmpty : undefined,
placeHolder: constants.enterConnectionStringEnvNameDescription placeHolder: constants.enterConnectionStringEnvNameDescription
} }
); );
@@ -128,15 +62,70 @@ export async function launchDeployDatabaseQuickpick(project: Project): Promise<I
} }
} }
return {
envVariableName: envVarName,
appSettingFile: settingExist ? localSettings : undefined,
appSettingType: settingExist ? AppSettingType.AzureFunction : AppSettingType.None
};
}
/**
* Create flow for publishing a database to docker container using only VS Code-native APIs such as QuickPick
*/
export async function launchPublishToDockerContainerQuickpick(project: Project): Promise<IDeployProfile | undefined> {
let localDbSetting: ILocalDbSetting | undefined;
// Deploy to docker selected
let portNumber = await vscode.window.showInputBox({
title: constants.enterPortNumber,
ignoreFocusOut: true,
value: constants.defaultPortNumber,
validateInput: input => !utils.validateSqlServerPortNumber(input) ? constants.portMustBeNumber : undefined
}
);
// Return when user hits escape
if (!portNumber) {
return undefined;
}
let password: string | undefined = '';
password = await vscode.window.showInputBox({
title: constants.enterPassword,
ignoreFocusOut: true,
value: password,
validateInput: input => utils.isEmptyString(input) ? constants.valueCannotBeEmpty : undefined,
password: true
}
);
// Return when user hits escape
if (!password) {
return undefined;
}
localDbSetting = {
serverName: 'localhost',
userName: 'sa',
dbName: project.projectFileName,
password: password,
port: +portNumber,
};
let deploySettings = await getPublishDatabaseSettings(project, false);
// Return when user hits escape
if (!deploySettings) {
return undefined;
}
if (localDbSetting && deploySettings) { if (localDbSetting && deploySettings) {
deploySettings.serverName = localDbSetting.serverName; deploySettings.serverName = localDbSetting.serverName;
} }
return { return {
localDbSetting: localDbSetting, localDbSetting: localDbSetting,
envVariableName: envVarName,
appSettingFile: settingExist ? localSettings : undefined,
deploySettings: deploySettings, deploySettings: deploySettings,
appSettingType: settingExist ? AppSettingType.AzureFunction : AppSettingType.None
}; };
} }

View File

@@ -207,6 +207,10 @@ export async function getPublishDatabaseSettings(project: Project, promptForConn
* Create flow for Publishing a database using only VS Code-native APIs such as QuickPick * Create flow for Publishing a database using only VS Code-native APIs such as QuickPick
*/ */
export async function launchPublishDatabaseQuickpick(project: Project, projectController: ProjectsController): Promise<void> { export async function launchPublishDatabaseQuickpick(project: Project, projectController: ProjectsController): Promise<void> {
const publishTarget = await launchPublishTargetOption();
if (publishTarget === constants.publishToDockerContainer) {
await projectController.publishToDockerContainer(project);
} else {
let settings: IDeploySettings | undefined = await getPublishDatabaseSettings(project); let settings: IDeploySettings | undefined = await getPublishDatabaseSettings(project);
if (settings) { if (settings) {
@@ -219,5 +223,21 @@ export async function launchPublishDatabaseQuickpick(project: Project, projectCo
} }
await projectController.publishOrScriptProject(project, settings, action === constants.publish); await projectController.publishOrScriptProject(project, settings, action === constants.publish);
} }
}
}
async function launchPublishTargetOption(): Promise<string | undefined> {
// Show options to user for deploy to existing server or docker
const publishOption = await vscode.window.showQuickPick(
[constants.publishToExistingServer, constants.publishToDockerContainer],
{ title: constants.selectPublishOption, ignoreFocusOut: true });
// Return when user hits escape
if (!publishOption) {
return undefined;
}
return publishOption;
} }

View File

@@ -7,6 +7,9 @@ export enum AppSettingType {
export interface IDeployProfile { export interface IDeployProfile {
localDbSetting?: ILocalDbSetting; localDbSetting?: ILocalDbSetting;
deploySettings?: IDeploySettings; deploySettings?: IDeploySettings;
}
export interface IDeployAppIntegrationProfile {
envVariableName?: string; envVariableName?: string;
appSettingFile?: string; appSettingFile?: string;
appSettingType: AppSettingType; appSettingType: AppSettingType;

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { AppSettingType, IDeployProfile, ILocalDbSetting } from './deployProfile'; import { AppSettingType, IDeployAppIntegrationProfile, IDeployProfile, ILocalDbSetting } from './deployProfile';
import * as UUID from 'vscode-languageclient/lib/utils/uuid'; import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import { Project } from '../project'; import { Project } from '../project';
import * as constants from '../../common/constants'; import * as constants from '../../common/constants';
@@ -32,7 +32,7 @@ export class DeployService {
return ''; return '';
} }
private findAppRuntime(profile: IDeployProfile, appSettingContent: any): string | undefined { private findAppRuntime(profile: IDeployAppIntegrationProfile, appSettingContent: any): string | undefined {
switch (profile.appSettingType) { switch (profile.appSettingType) {
case AppSettingType.AzureFunction: case AppSettingType.AzureFunction:
return <string>appSettingContent?.Values['FUNCTIONS_WORKER_RUNTIME']; return <string>appSettingContent?.Values['FUNCTIONS_WORKER_RUNTIME'];
@@ -41,7 +41,7 @@ export class DeployService {
return undefined; return undefined;
} }
public async updateAppSettings(profile: IDeployProfile): Promise<void> { public async updateAppSettings(profile: IDeployAppIntegrationProfile, deployProfile: IDeployProfile | undefined): Promise<void> {
// Update app settings // Update app settings
// //
if (!profile.appSettingFile) { if (!profile.appSettingFile) {
@@ -53,22 +53,22 @@ export class DeployService {
let content = JSON.parse(fse.readFileSync(profile.appSettingFile, 'utf8')); let content = JSON.parse(fse.readFileSync(profile.appSettingFile, 'utf8'));
if (content && content.Values) { if (content && content.Values) {
let connectionString: string | undefined = ''; let connectionString: string | undefined = '';
if (profile.localDbSetting) { if (deployProfile && deployProfile.localDbSetting) {
// Find the runtime and generate the connection string for the runtime // Find the runtime and generate the connection string for the runtime
// //
const runtime = this.findAppRuntime(profile, content); const runtime = this.findAppRuntime(profile, content);
let connectionStringTemplate = this.createConnectionStringTemplate(runtime); let connectionStringTemplate = this.createConnectionStringTemplate(runtime);
const macroDict: Record<string, string> = { const macroDict: Record<string, string> = {
'SERVER': profile?.localDbSetting?.serverName || '', 'SERVER': deployProfile?.localDbSetting?.serverName || '',
'PORT': profile?.localDbSetting?.port?.toString() || '', 'PORT': deployProfile?.localDbSetting?.port?.toString() || '',
'USER': profile?.localDbSetting?.userName || '', 'USER': deployProfile?.localDbSetting?.userName || '',
'SA_PASSWORD': profile?.localDbSetting?.password || '', 'SA_PASSWORD': deployProfile?.localDbSetting?.password || '',
'DATABASE': profile?.localDbSetting?.dbName || '', 'DATABASE': deployProfile?.localDbSetting?.dbName || '',
}; };
connectionString = templates.macroExpansion(connectionStringTemplate, macroDict); connectionString = templates.macroExpansion(connectionStringTemplate, macroDict);
} else if (profile.deploySettings?.connectionUri) { } else if (deployProfile?.deploySettings?.connectionUri) {
connectionString = await this.getConnectionString(profile.deploySettings?.connectionUri); connectionString = await this.getConnectionString(deployProfile?.deploySettings?.connectionUri);
} }
if (connectionString && profile.envVariableName) { if (connectionString && profile.envVariableName) {
@@ -88,8 +88,8 @@ export class DeployService {
return undefined; return undefined;
} }
const projectName = project.projectFileName; const projectName = project.projectFileName;
const imageLabel = `${constants.dockerImageLabelPrefix}_${projectName}`; const imageLabel = `${constants.dockerImageLabelPrefix}_${projectName}`.toLocaleLowerCase();
const imageName = `${constants.dockerImageNamePrefix}-${projectName}-${UUID.generateUuid().toLowerCase()}`; const imageName = `${constants.dockerImageNamePrefix}-${projectName}-${UUID.generateUuid()}`.toLocaleLowerCase();
const root = project.projectFolderPath; const root = project.projectFolderPath;
const mssqlFolderPath = path.join(root, constants.mssqlFolderName); const mssqlFolderPath = path.join(root, constants.mssqlFolderName);
const commandsFolderPath = path.join(mssqlFolderPath, constants.commandsFolderName); const commandsFolderPath = path.join(mssqlFolderPath, constants.commandsFolderName);

View File

@@ -66,10 +66,6 @@ describe('deploy service', function (): void {
it('Should deploy a database to docker container successfully', async function (): Promise<void> { it('Should deploy a database to docker container successfully', async function (): Promise<void> {
const testContext = createContext(); const testContext = createContext();
const deployProfile: IDeployProfile = { const deployProfile: IDeployProfile = {
appSettingType: AppSettingType.AzureFunction,
appSettingFile: '',
deploySettings: undefined,
envVariableName: '',
localDbSetting: { localDbSetting: {
dbName: 'test', dbName: 'test',
password: 'PLACEHOLDER', password: 'PLACEHOLDER',
@@ -137,10 +133,6 @@ describe('deploy service', function (): void {
await fse.writeFile(filePath, settingContent); await fse.writeFile(filePath, settingContent);
const deployProfile: IDeployProfile = { const deployProfile: IDeployProfile = {
appSettingType: AppSettingType.AzureFunction,
appSettingFile: filePath,
deploySettings: undefined,
envVariableName: 'SQLConnectionString',
localDbSetting: { localDbSetting: {
dbName: 'test', dbName: 'test',
password: 'PLACEHOLDER', password: 'PLACEHOLDER',
@@ -150,9 +142,14 @@ describe('deploy service', function (): void {
} }
}; };
const appInteg = {appSettingType: AppSettingType.AzureFunction,
appSettingFile: filePath,
deploySettings: undefined,
envVariableName: 'SQLConnectionString'};
const deployService = new DeployService(testContext.outputChannel); const deployService = new DeployService(testContext.outputChannel);
sandbox.stub(childProcess, 'exec').yields(undefined, 'id'); sandbox.stub(childProcess, 'exec').yields(undefined, 'id');
await deployService.updateAppSettings(deployProfile); await deployService.updateAppSettings(appInteg, deployProfile);
let newContent = JSON.parse(fse.readFileSync(filePath, 'utf8')); let newContent = JSON.parse(fse.readFileSync(filePath, 'utf8'));
should(newContent).deepEqual(expected); should(newContent).deepEqual(expected);
@@ -184,23 +181,26 @@ describe('deploy service', function (): void {
await fse.writeFile(filePath, settingContent); await fse.writeFile(filePath, settingContent);
const deployProfile: IDeployProfile = { const deployProfile: IDeployProfile = {
appSettingType: AppSettingType.AzureFunction,
appSettingFile: filePath,
deploySettings: { deploySettings: {
connectionUri: 'connection', connectionUri: 'connection',
databaseName: 'test', databaseName: 'test',
serverName: 'test' serverName: 'test'
}, },
envVariableName: 'SQLConnectionString',
localDbSetting: undefined localDbSetting: undefined
}; };
const appInteg = {
appSettingType: AppSettingType.AzureFunction,
appSettingFile: filePath,
envVariableName: 'SQLConnectionString',
}
const deployService = new DeployService(testContext.outputChannel); const deployService = new DeployService(testContext.outputChannel);
let connection = new azdata.connection.ConnectionProfile(); let connection = new azdata.connection.ConnectionProfile();
sandbox.stub(azdata.connection, 'getConnection').returns(Promise.resolve(connection)); sandbox.stub(azdata.connection, 'getConnection').returns(Promise.resolve(connection));
sandbox.stub(childProcess, 'exec').yields(undefined, 'id'); sandbox.stub(childProcess, 'exec').yields(undefined, 'id');
sandbox.stub(azdata.connection, 'getConnectionString').returns(Promise.resolve('connectionString')); sandbox.stub(azdata.connection, 'getConnectionString').returns(Promise.resolve('connectionString'));
await deployService.updateAppSettings(deployProfile); await deployService.updateAppSettings(appInteg, deployProfile);
let newContent = JSON.parse(fse.readFileSync(filePath, 'utf8')); let newContent = JSON.parse(fse.readFileSync(filePath, 'utf8'));
should(newContent).deepEqual(expected); should(newContent).deepEqual(expected);

View File

@@ -7,7 +7,7 @@ import * as should from 'should';
import * as path from 'path'; import * as path from 'path';
import * as os from 'os'; import * as os from 'os';
import { createDummyFileStructure } from './testUtils'; import { createDummyFileStructure } from './testUtils';
import { exists, trimUri, removeSqlCmdVariableFormatting, formatSqlCmdVariable, isValidSqlCmdVariableName, timeConversion } from '../common/utils'; import { exists, trimUri, removeSqlCmdVariableFormatting, formatSqlCmdVariable, isValidSqlCmdVariableName, timeConversion, validateSqlServerPortNumber, isEmptyString } from '../common/utils';
import { Uri } from 'vscode'; import { Uri } from 'vscode';
describe('Tests to verify utils functions', function (): void { describe('Tests to verify utils functions', function (): void {
@@ -88,5 +88,22 @@ describe('Tests to verify utils functions', function (): void {
should(timeConversion( (59 * 1000))).equal('59 sec'); should(timeConversion( (59 * 1000))).equal('59 sec');
should(timeConversion( (59))).equal('59 msec'); should(timeConversion( (59))).equal('59 msec');
}); });
it('Should validate port number correctly', () => {
should(validateSqlServerPortNumber('invalid')).equals(false);
should(validateSqlServerPortNumber('')).equals(false);
should(validateSqlServerPortNumber(undefined)).equals(false);
should(validateSqlServerPortNumber('65536')).equals(false);
should(validateSqlServerPortNumber('-1')).equals(false);
should(validateSqlServerPortNumber('65530')).equals(true);
should(validateSqlServerPortNumber('1533')).equals(true);
});
it('Should validate empty string correctly', () => {
should(isEmptyString('invalid')).equals(false);
should(isEmptyString('')).equals(true);
should(isEmptyString(undefined)).equals(true);
should(isEmptyString('65536')).equals(false);
});
}); });

View File

@@ -600,11 +600,6 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
generate-password@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/generate-password/-/generate-password-1.6.0.tgz#9dff4fcca7f831e6832d464509910fab333d0323"
integrity sha512-YUJTQkApkLT/fru0QdYWP0lVZdPKhF5kXCP24sgI4gR/vFMJFopCj5t1+9FAKIYcML/nxzx2PMkA1ymO1FC+tQ==
gensync@^1.0.0-beta.1: gensync@^1.0.0-beta.1:
version "1.0.0-beta.1" version "1.0.0-beta.1"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"